Rss

Vertically aligning patterns in code

Jardin à la Française

Aligning similar textual elements can drastically improve readability as well as help spot typos, at the small cost of a few extra spaces or lines. Typos will not always be picked up by the compiler e.g. if it’s a variable built on a pattern but falls victim to a typo.


Note 1: For some reason, aligning similar elements is a practise I have rarely seen used. I guess people are just not willing to put the extra formatting effort for what they would deem, I guess, too small a visual improvement. Being of the visual-type myself I of course find the improvement quite valuable.

Note 2 : this tip has similar mechanics to the “space vs tab” question.

In practise

As much as possible try aligning similar patterns when close to each other in your code layout. Sometimes you will have to decide what elements to align, to the detriment of others, or may have to break vertical alignment of line starts, or space items in seemingly bizarre fashion. But this is all for the superior sake of improved overall readability.

Let’s take a look at 2 examples illustrating this philosophy:

  • Not aligned:
if (isset($arr_opt_params['params_from_script_options']))
{ if (!$arr_opt_params['params_from_script_options'] instanceof FPL__Type_Boolean)
  { throw new FPL__Logger__Exception__Construct($err_msg.' params_from_script_options');
  }
  else $this->loggerMinLevel=$arr_opt_params['params_from_script_options'];
}

if (isset($arr_opt_params['min_level']))
{ if (!$arr_opt_params['min_level'] instanceof FPL__Type_Logger_Debug_Level)
  { throw new FPL__Logger__Exception__Construct($err_msg.' min_level');
  }
  else $this->loggerMinLevel=$arr_opt_params['min_level'];
}
  • Aligned:
if (isset($arr_opt_params['params_from_script_options']))
{ if    (!$arr_opt_params['params_from_script_options'] instanceof FPL__Type_Boolean)
  { throw new FPL__Logger__Exception__Construct($err_msg.' '.
                          'params_from_script_options');
  }
  else $this->loggerParamsFromScriptOptions=
          $arr_opt_params['params_from_script_options'];
}

if (isset($arr_opt_params['min_level'                 ]))
{ if    (!$arr_opt_params['min_level'                 ] instanceof FPL__Type_Logger_Debug_Level)
  { throw new FPL__Logger__Exception__Construct($err_msg.' '.
                          'min_level'                 );
  }
  else $this->loggerMinLevel=
          $arr_opt_params['min_level'                 ];
}

Here, it was decided to place the emphasis on the keys of the array, params_from_script_options and min_level. Notice how these elements appear aligned throughout the block whenever they appear and whatever their then semantic value (array index, subfix…), and this so across interspersed ligns too (the eye, when perusing the code vertically, will easily skip these ligns). Notice also that the array brackets are aligned: when possible, align as much common material as possible.

Other example

  • Not aligned:
ini_set('display_errors',$this->phpDisplayErrors->val());
ini_set('log_errors',$this->phpLogErrors->val());
ini_set('error_log',$this->phpErrorLog);
error_reporting($this->phpErrorReporting->val());
ini_set('log_errors_max_len',$this->phpLogErrorsMaxLen->val());
ini_set('track_errors',$this->phpTrackErrors->val());
ini_set('ignore_repeated_errors',$this->phpIgnoreRepeatedErrors->val());
  • Aligned:
ini_set('display_errors'        , $this->phpDisplayErrors       ->val() );
ini_set('log_errors'            , $this->phpLogErrors           ->val() );
ini_set('error_log'             , $this->phpErrorLog                    );
error_reporting(                  $this->phpErrorReporting      ->val() );
ini_set('log_errors_max_len'    , $this->phpLogErrorsMaxLen     ->val() );
ini_set('track_errors'          , $this->phpTrackErrors         ->val() );
ini_set('ignore_repeated_errors', $this->phpIgnoreRepeatedErrors->val() );

In this last example, the alignement of similiar textual elements also allows us to use the Column Editing mode (Alt + C in UltraEdit, 'Toggle Block Selection Mode' in Eclipse) and powerfully edit all ligns at the same time.

Taking matters further

	...
	<table>
		<tr>
			<s:select    cssClass="js__strat__cible_input_type       "       label='%{getText("strategieBo.cibles[].type"       )}' name="strategieBo.cibles[%{#index}].type"                     list="getListValues('strategieBo.cibles[].type')"             headerKey="" headerValue="%{getText('label.select.header.default')}" onchange="com.sfr.project.concerns.strategie.onChangeCibleType(this);"     disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_messageDiss"       label='%{getText("strategieBo.cibles[].messageDiss")}' name="strategieBo.cibles[%{#index}].messageDiss"              list="getListValues('strategieBo.cibles[].messageDiss')"      headerKey="" headerValue="%{getText('label.select.header.default')}"                                                                            disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_numero     "       label='%{getText("strategieBo.cibles[].numero"     )}' name="strategieBo.cibles[%{#index}].numero"                   value="%{numero}"                                                                                                                                                                                             disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_compMag    "       label='%{getText("strategieBo.cibles[].compMag"    )}' name="strategieBo.cibles[%{#index}].compMag"                  list="getListValues('strategieBo.cibles[].compMag')"          headerKey="" headerValue="%{getText('label.select.header.default')}"                                                                            disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_compServ   "       label='%{getText("strategieBo.cibles[].compServ"   )}' name="strategieBo.cibles[%{#index}].compServ"                 value="%{compServ}"                                                                                                                                                                                           disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_action     "       label='%{getText("strategieBo.cibles[].action"     )}' name="strategieBo.cibles[%{#index}].action"                   list="getListValues('strategieBo.cibles[].action')"           headerKey="" headerValue="%{getText('label.select.header.default')}" onchange="com.sfr.project.concerns.strategie.onChangeCibleAction(this);"   disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_seuilDeb   "       label='%{getText("strategieBo.cibles[].seuilDeb"   )}' name="strategieBo.cibles[%{#index}].seuilDeb"                 value="%{seuilDeb}"                                                                                                                                                                                           disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_numInternes"       label='%{getText("strategieBo.cibles[].numInternes")}' name="strategieBo.cibles[%{#index}].numInternes"              value="%{numInternes}"                                                                                                                                                                                        disabled="%{#is_template}" />
		</tr>
	</table>
	...

In the above example (jsp with struts2 tags), I have sacrificed line length to readability/editability, not shying away from stretching lines all the way up to 425 characters. That’s a far cry from the “recommended” 80! (I can only imagine the Haughty Keepers of Ye Olde Code Conventions frowning and shaking their heads at this point).

However, 2 things to consider:

  • We are here in the presence of a description file. It’s not a function you have to read to understand. You don’t need to take that horizontal scrollbar all the way east to figure out what it’s all about. It’s just some lengthy, repetitive, error-prone boilerplate code. So it makes sense to decide to facilitate editing (hello again column mode) by exhibiting the similarities from one line to the next through vertical alignment.
  • Also, it really compresses the visual height of the file (I’m talking number of lines), maximizing the amount of material you can squeeze on your screen, giving you a better bird’s eye view of the whole thing. Advantages are numerous.

Let’s take a look what that piece of code would look like in ‘usual’ style:

	...
	<table>
		<tr>
			<s:select    cssClass="js__strat__cible_input_type"
			   label='%{getText("strategieBo.cibles[].type")}'
			   name="strategieBo.cibles[%{#index}].type"
			   list="getListValues('strategieBo.cibles[].type')"
			   headerKey="" headerValue="%{getText('label.select.header.default')}"
			   onchange="com.sfr.project.concerns.strategie.onChangeCibleType(this);"
			   disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_messageDiss"
			   label='%{getText("strategieBo.cibles[].messageDiss")}'
			   name="strategieBo.cibles[%{#index}].messageDiss"
			   list="getListValues('strategieBo.cibles[].messageDiss')"
			   headerKey=""
			   headerValue="%{getText('label.select.header.default')}"
			   disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_numero"
			   label='%{getText("strategieBo.cibles[].numero")}'
			   name="strategieBo.cibles[%{#index}].numero"
			   value="%{numero}"
			   disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_compMag"
			   label='%{getText("strategieBo.cibles[].compMag")}'
			   name="strategieBo.cibles[%{#index}].compMag"
			   list="getListValues('strategieBo.cibles[].compMag')"
			   headerKey="" headerValue="%{getText('label.select.header.default')}"
			   disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_compServ"
			   label='%{getText("strategieBo.cibles[].compServ")}'
			   name="strategieBo.cibles[%{#index}].compServ"
			   value="%{compServ}"
			   disabled="%{#is_template}" />
			<s:select    cssClass="js__strat__cible_input_action"
			   label='%{getText("strategieBo.cibles[].action")}'
			   name="strategieBo.cibles[%{#index}].action"
			   list="getListValues('strategieBo.cibles[].action')"
			   headerKey=""
			   headerValue="%{getText('label.select.header.default')}"
			   onchange="com.sfr.project.concerns.strategie.onChangeCibleAction(this);"
			   disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_seuilDeb"
			   label='%{getText("strategieBo.cibles[].seuilDeb")}'
			   name="strategieBo.cibles[%{#index}].seuilDeb"
			   value="%{seuilDeb}"disabled="%{#is_template}" />
			<s:textfield cssClass="js__strat__cible_input_numInternes"
			   label='%{getText("strategieBo.cibles[].numInternes")}'
			   name="strategieBo.cibles[%{#index}].numInternes"
			   value="%{numInternes}"
			   disabled="%{#is_template}" />
		</tr>
	</table>
	...

Now which one of the 2 would you rather edit/maintain? I know which one I would choose. (of course I do, I’m writing this very article). Is the 80-character line-length diktat really worth the mess?

Other views on the subject

I think it’s always beneficial to listen to the voices of people with a different point of view. It’ll either reinforce yours, and you’ll know why, or help you mitigate it, giving you a broader, more pragmatic vision of the subject.

So without further ado, here is what Robert Martin has to say:  (Note:  Robert Martin speaks of ‘Horizontal Alignment’. I can only imagine that’s a slip of the tongue. That or I have spatial issues)

Clean-code-handbook_Horizontal-aligning
[Clean Code: A Handbook of Agile Software Craftsmanship - Robert C. Martin, Prentice Hall, 2009]

Not my point of view, but I can see what he’s getting at.

In short:

  1. Vertically align similar patterns occurring in your code
  2. Patterns need not be strictly identical, just similar
  3. Try to align the longest matching patterns
  4. Take advantage of your IDE’s column editing mode
  5. Use in places where it really makes a difference
francois

francois

enjoys reading, thinking and talking about all matters pertaining to software, alongside a whole bunch of other stuff (not pictured here). Please check out the "About" section for more.

More Posts

Comments (6)

  1. I agree with you 100%. I’ve used this alignment technique for a long time and I find it much easier to read code written that way.

  2. Note if you have many developers working on a large code base, this falls apart quickly and makes diffs and small changes difficult to detect. Hence the reason Google’s style guides also recommend against it.

    http://google-styleguide.googlecode.com/svn/trunk/javaguide.html#s4.6.3-horizontal-alignment

    • francois francois

      Thanks for the interesting link.
      Actually from what I’m reading there, they do not “recommend against it” as you say, they merely “permit” without “requiring”. Obviously it’s hard to disagree with the points made. That’s why I usually adopt the same policy: if you’re the original developer, align at will, until commit. Allow for some padding space to accommodate for leeway in terms of change. If you’re bringing cherry-picked modifications to a committed file, do not reformat.

  3. Stan V

    So today I needed to align the validation rules of a giant form so I can read them. I instantly recognized aligning them by hand is an absolute waste of time… so I wrote myself a tool to do it for me.

    For a big supporter of alignment, I find it odd you don’t mention how to automate it. I mean you don’t suggest we align and re-align our code EVERY TIME we make a tiny change, right?

    • francois francois

      Thanks for your comment, Stan.
      To that I would reply two things.
      1. On automation: I use the column mode! (available in all good editors – sometimes also called “rectangular selection”). This lets me align stuff very quickly. Sometimes I also use previously saved regexps.

      2. Remember a developer spends 80% (or whatever other high figure) of his time reading code. Not writing it. It’s a fallacy to think “oh this will slow me down writing my code”. Code clarity (by all means) and good tools are essential in that respect.

      “Every time you make a tiny change” … not necessarily. I mean, I allow enough padding in between elements on one line to accommodate for changes (if you lengthen some variable’s name, then you’ll do that to the expense of some blank space after it). And, whenever I find myself in the case where a change breaks the alignment and would require having the whole block reformated, well, I just usually leave it at that. If it’s just the one, it’s usually fine. Readbility stays good. Again, it’s all a question – as usual – of hitting the right spot between what you might offer (readability) and its cost (the “hassle” of aligning).

Leave a Reply

Your email address will not be published. Required fields are marked *