Sunday, October 21, 2012

A Proper Cygwin Environment

I've been spending more time in Cygwin lately, after having decided to wean myself from Ye Olde Windows Command Prompt. (Actually, I've been using 4NT since back in the day when it was called 4DOS and ran as a replacement for COMMAND.COM.) As with anything else I use, "out of the box" is just a starting point, and I've spent some time creating a proper Cygwin environment. Below is a description of my setup.

Mapping Cygdrive

By default, Cygwin mounts all Windows drives under the /cygdrive/ mount point. Who wants to look at and type that all day? Certainly not me. Edit /etc/fstab and replace the existing line:
none /cygdrive cygdrive binary,posix=0,user 0 0
with:
none / cygdrive binary,posix=0,user 0 0 
Now I can type cd /c/Users/ instead of cd /cygdrive/c/Users.


Mintty

Cygwin normally runs in a standard Windows command window. There are better ways to run Cygwin, and Mintty is one of the best. Bring up an Explorer window, navigate to cygwin/bin, and find mintty.exe. Make a shortcut and move it someplace more convenient. To make Mintty read your ~/.bash_profile you'll need to edit the command line parameters. Right-click your shortcut and select Properties. Add -e /bin/bash --login to the end of the command line. Now bash will run your ~/.bash_profile when you start Mintty.


Colors and Fonts

(Colors and fonts are an awfully subjective matter, but this section is as much about how to set things up as it is about which colors and fonts I like.)

After a bit of initial doubt, I've become a fan of the Solarized color scheme. A tip of the hat to Ethan for creating a very readable color scheme that can be applied to just about any application. So let's Solarize Cygwin.


Mintty

I have much love for Mintty, but its default colors are boring. To Solarize them, go to mintty-colors-solarized. Select either .mintty--solarized-dark or .mintty--solarized-light and insert the contents of this file into your ~/.minttyrc file. (Which may not yet exist.) You might also add the line Term=xterm-256color if you intend to use Solarized in vim; see below for more on that.

dircolors (ls)

Solarized directory colors can be found at dircolors-solarized. I've put the contents of dircolors.ansi-dark into my ~/.dir_colors. Set up an alias for ls and evaluate dircolors in ~/.bashrc:
eval `dircolors ~/.dir_colors`
...
alias ls="ls --color" 


vim

I'm sure Emacs is wonderful, I just never learned it. Terribly sorry, but you'll have to go elsewhere to get your Solarized Emacs love. Now, for those of you still reading, go to vim-colors-solarized and get colors/solarized.vim. Put this file into ~/.vim/colors. Edit (create if necessary) ~/.vimrc and ensure it contains:
syntax enable
set hlsearch
set background=dark
let g:solarized_termcolors=256
let g:solarized_termtrans=1
colorscheme solarized
That wasn't so hard, was it?

Fonts

There are plenty of monospaced fonts available on the web, some very good. I have been going back and forth between Consolas and Adobe's recently released Source Code Pro. Both are very well designed; I find them to be equally readable, and I'm pretty picky about fonts. Another very good font is Proggy, which I used for years. At the moment I'm using Consolas at 9 point bold, but that may change....

August 9 2013 Update


Customizing ~/.bashrc for Fun and Profit


Google "customizing bashrc" and you can go crazy; there are a lot of examples out there. This page has a bunch if you have a few minutes of idle time to waste. Here's a pro tip: don't go crazy adding a bunch of aliases, functions, and custom Bash configuration variables. Instead, add things a little at a time, over time, as you discover a need for them (where "need" can be a pretty low barrier, i.e. I've added an alias after spending a couple minutes figuring out a complicated command because I know I'll need it again). Okay, here are the more important bits of my ~/.bashrc, broken up into bite-sized pieces:

# Return immediately if we are not interactive
[ -z "$PS1" ] && return


The first thing we do is exit if we're in a non-interactive shell. This is kind of important. (And if you intend to use Cygwin/Bash in earnest you will need to understand the difference between an interactive and non-interactive shell.) This next block is dedicated to setting the window title and Bash prompt.

# get current git branch name
function git_branch {
    export gitbranch=[$(git rev-parse --abbrev-ref HEAD 2>/dev/null)]
    if [ "$?" -ne 0 ]
      then gitbranch=
    fi
    if [[ "${gitbranch}" == "[]" ]]
      then gitbranch=
    fi
}

# set usercolor based on whether we are running with Admin privs
function user_color {
    id | grep "Admin" > /dev/null
    RETVAL=$?
    if [[ $RETVAL == 0 ]]; then
        usercolor="[0;35m";
    else
        usercolor="[0;32m";
    fi
}

# set TTYNAME
function ttyname() { export TTYNAME=$@; }

# Set prompt and window title
inputcolor='[0;37m'
cwdcolor='[0;34m'
gitcolor='[1;31m'
user_color

# Setup for window title
export TTYNAME=$$
function settitle() {
  p=$(pwd);
  let l=${#p}-25
  if [ "$l" -gt "0" ]; then
    p=..${p:${l}}
  fi
  t="$TTYNAME $p"
  echo -ne "\e]2;$t\a\e]1;$t\a";
}

PROMPT_COMMAND='settitle; git_branch; history -a;'
export PS1='\[\e${usercolor}\][\u]\[\e${gitcolor}\]${gitbranch}\[\e${cwdcolor}\][$PWD]\[\e${inputcolor}\] $ '
}


There's rather a lot going on here. The important lines are PROMPT_COMMAND and export PS1. PROMPT_COMMAND is a command run by Bash after each command returns. When you type ls <Enter>, the PROMPT_COMMAND is run just after ls finishes executing, before the prompt is displayed. My prompt command (1) sets the window title, (2) gets the current Git branch, and (3) flushes the history. Setting the window title takes a little work. I'm setting it to the $TTYNAME variable followed by the last 25 characters of the current working directory. By default, $TTYNAME is the term's pid, but I can use e.g. ttyname SomeProject to change it to something more meaningful. Why go to all this trouble? So that when I have multiple terms open and want to switch between them, I can see the window title when I use Alt-Esc or hover over the taskbar mintty icons. Instead of randomly switching between terms all named "-bash" and hoping I pick the right one.

PS1 aka The Bash Prompt

There are a lot of interesting, cool, informative, funny, unique examples of Bash prompts on the web. Here's mine as set in the above export PS1 command:

export PS1=\[\e${usercolor}\][\u]\[\e${gitcolor}\]${gitbranch}\[\e${cwdcolor}\][$PWD]\[\e${inputcolor}\] $

And here's an example of what it looks like:




Let me break that down a little. The first part, "twheeler", is my username (\u), printed in yellow when I'm a normal user and purple when I open a term as an administrator (right-click on your mintty icon and select Run As Administrator...). Just so I know when I'm a god on the machine. The user_color function sets the usercolor variable to the right ANSI color sequence based on my privileges, and it's then echoed in the prompt with ${usercolor}.

Next is the current Git branch I'm working in, if the current directory is part of a Git project. The git_branch function, called by PROMPT_COMMAND, helps set this up, and it's echoed with ${gitcolor}.

Finally I print the current directory with $PWD and end it all with a string and space.

The next part of my .bashrc sets up some history settings:

# Bash history settings
export HISTFILESIZE=1000000
export HISTSIZE=100000
export HISTCONTROL=ignorespace
export HISTIGNORE='ls:history:ll'
export HISTTIMEFORMAT='%F %T '
# Append to bash_history instead of overwriting
shopt -s histappend

For another take on Cygwin setup check out this page.

Friday, October 19, 2012

Sources of Technical Debt and Bad Design

As a software 'craftsman' I appreciate the intrinsic beauty of good design. And I have an internal, almost instinctive, dislike of poor design. It's like seeing graffiti on the wall of a church or listening to someone sing out of key. This alone is enough to make me want to write good code, and fix bad code. But design is about much more than aesthetics. It takes a lot less time and effort (costs less) to add features to a project that has a good design. And that's good for everyone -- developers, management, and users.

(By the way, when I say "design" I mean the entire application. Not just the Java or C# code, but also the build environment/tools, database scripts, shell scripts, etc.)

So why do so many projects end up with a horrible design and a mountain of technical debt?

No team sets out to create a poor design. But many (perhaps most) projects end up being difficult to understand and modify. Some eventually end up with so much technical debt that the team or management decide it's better to start from scratch. (Which is often another sort of mistake.)

So where does bad design and tech debt come from? Here are some causes I'm familiar with:
  • Time constraints. In the midst of getting an application to build and work right, we often take shortcuts. Another name for 'shortcut' is 'technical debt'. Technical debt is usually rationalized as being necessary to meet a deadline. Sadly, just about every excuse or justification we use to "save time" winds up costing us or our beloved colleagues much more time tomorrow as we saved today. Another way of saying "we don't have time to clean up the code" is "it's going to take twice as long to add a new feature in six months as it does today".
  • Personnel changes. New team members can be great contributors to bad design, by writing code that is incompatible with or breaks the existing design. Maybe because they don't understand it, maybe because they think they have a better idea (ego-driven programming), maybe because they're thrown into the fire without any help. "Okay Steve, your first assignment is to add history tracking to changes to the model so the user can revert commands. I'd work with you on it but don't really have time. You can stop by if you have a question, though."
  • New requirements. Sometimes a new requirement comes along which is incompatible with the existing design. The right way to handle this is to refactor the existing code towards a new design that fits both the existing features and the new one. The more common way to handle this is to "tack on" the new feature by hot-gluing it to the side of the existing code and slapping some paint on it.
  • Inexperience and ignorance. Whether it's a new technology we haven't used, a domain we're not familiar with, or a general lack of experience in development, not knowing the "right" way to get it done will almost certainly contribute to a bad design and/or technical debt.
  • Familiarity. Familiarity tends to create blind spots and acceptance of bad design and technical debt. You get used to dealing with the randomly failing unit tests, or the bizarre class hierarchy, or the 15 minute deployment cycle. And you forget that it's really pretty messed up and needs to be fixed.

Nobody prioritizes bad design or technical debt; it happens as a side effect of decisions we make every day. Sometimes it's an intentional decision, possibly made with an understanding of the consequences and with the best of intentions to go back and do the rework necessary to clean things up later. Sometimes the decision is made unconsciously or in ignorance of the consequences.

The only way I know of to prevent and eliminate technical debt is to be intentional about removing it when you see it. It's like housekeeping and home maintenance: you dust and vacuum and sweep, pick up and put away tools and movies and toys, and replace the old weatherproofing around the door. The most successful teams I've been part of are good at housekeeping: they have been disciplined about using best practices to keep technical debt at bay and maintain a good design. (And by the way, when I say "team" I mean everyone who is part of the project -- programmers, documentation, QA, UX, BA's, and management.)

So do your chores and keep the house clean. It's worth the effort.