A framework for shipping high quality software


Software engineers, technical leads and managers all share one goal – shipping high-quality software on time. Ambiguous requirements, strict deadlines and technical debt exert conflicting tugs on a software team’s priorities. Software quality has to be great otherwise bugs inundate the team; further slowing down delivery speed.

This post proposes a model for consistently shipping high-quality software. It also provides a common vocabulary for communication across teams and people.

Origins

This framework is the culmination of lessons learnt delivering the most challenging project I have ever worked on. The task was to make a web application globally available to meet scaling and compliance requirements.

The one-line goal quickly ballooned into a multi-month effort requiring:

  • Moving from a single compute resource based approach to multiple compute resources.
  • Fundamental changes to platform-level components across all micro services.
  • Constantly collaborating with diverse teams to get more insight.

The icing on the cake? All critical deployments had to be seamless and not cause a service outage.

What’s Donald Rumsfeld gotta do with software?

He’s not a software engineer but his quote below provides the basis.

There are known knowns. These are things we know that we know. There are known unknowns. That is to say, there are things that we know we don’t know. But there are also unknown unknowns. There are things we don’t know we don’t know.

– Donald Rumsfeld

His quote is a simplified version of the Johari window from Psychology. Applying this to software, the window would look thus:

What the developer knows What the developer doesn’t know
What other developers know Known Unknown known
What other developers don’t know Known Unknown Unknown Unknown

1. The known

Feature requirements, bugs, customer requests etc. These are the concepts that are well-known and expected. However, writing code to implement a feature may not guarantee a full known status. For example, untested code can still be a known unknown until you guarantee how it works.

It is one thing to think code works as you think it would and it is another to prove it. Unit tests, functional tests and even manually stepping through every line help to increase the known.

2. The known unknown and the unknown known

I am collapsing both halves into one group because they are related.

  1. Known Unknown

    These are aspects that the developer knows about but other engineers in partner teams don’t know. A good example would be creating a replacement API and making an existing one obsolete. Another example would be changing the behaviour of some shared component.

  2. Unknown Known

    These are the aspects that the developer doesn’t know but engineers in other teams know about. For example, a seemingly minor update of a core component by a developer can trigger expensive rewrite cascades in partner teams. Another example could be quirks that are known to only a few engineers.

Clear communication is the one good fix for challenges in this category. Over-communicate! Send out emails, hold design reviews and continuously engage stakeholders.

This is extra important for changes with far-reaching impact. As the developer/lead/manager, you need to spend time with the key folks and understand their scenarios deeply. This would lead to better models as well as help forecast issues that might arise.

Finally, this applies to customers too – you may what the customer doesn’t know about and vice versa.

3. The unknown unknowns

This is the most challenging category. There is no way to model or prepare for something unpredictable – an event that has never happened before. Unknown Unknowns (UUs) include hacks, data loss / corruption, theft, sabotage, release bugs and so on.

Don’t fret it yet, the impact of UUs can be easily mitigated. Let’s take two more metrics:

  1. Mean time to repair (MTTR)

    The average amount of time it takes to repair an issue with the software.

  2. Mean time to detect (MTTD)

    The average amount of time it takes to detect a flaw.

The most reliable way of limiting the impact of UUs is to keep the MTTR and MTTD low. Compare the damage that a data-corrupting deployment can cause in 5 minutes versus 1 hour.

MTTD

A rich monitoring and telemetry system is essential for lowering MTTD metrics. Log compute system health usage metrics (RAM, CPU, disk reads etc), HTTP request statuses (500s, 400s etc.) and more.

Ideally, a bad release will trigger alarms and notify administrators immediately it goes out. This will enable the service owner to react and recover.

MTTR

Having a feature toggle or flighting system can help with MTTR metrics. Again using the bad release example, a flight/feature toggle will enable you to ‘turn off’ that feature before it causes irreparable damage.

Also critical is having a quick release pipeline, if it takes two days to get a fix out; then your MTTR is 2 days+x. That’s a red flag – invest in a CI pipeline.

tldr?

A software engineer is rolling out a critical core update, a few questions to ask:

  • Does he have enough logging to be able to debug and track issues if they arise?
  • Is the risky feature behind a flight or feature toggle? How soon can it be turned off if something goes wrong?
  • Are there metrics that can be used to find out if something goes wrong after the feature is deployed in production?

A release strategy is to roll out the feature in a turned off state and then turn it on for a few people and see if things are stable. If it fails, then you turn off the feature switch and fix the issue. Otherwise, you progressively roll out to more users.

What steps do you take to ensure software quality?

Related

  1. Creating Great User Experiences
  2. Efficiently shipping Big Hairy Audacious Software projects
  3. Things to check before releasing your web application
Advertisements

Book Review:Build your own AngularJS


As part of my continuous learning; I started reading Tero Parviainen‘s ‘Build your own AngularJS‘ about 6 months ago. After 6 months and 127 commits, I am grateful I completed the book.

While I didn’t take notes while reading, some ideas stood out. Thus, this post describes some of the concepts I have picked up from the book.

The Good

1. Get the foundational concepts right

This appears to be a recurring theme as I learn more about software engineering. Just as I discovered while reading the SICP classic, nailing the right abstractions for the building bricks makes software easy to build and extend.

Angular has support for transclusion which allows directives to do whatever they want with some piece of DOM structure. A tricky concept but very powerful since it allows you to clone and manage the scope in transcluded content.

There is also support for element transclusion. Unlike the regular transclude which will include some DOM structure in some new location; element transclusion provides control over the element itself.

So why is this important? Imagine you can add this to some element to only show up under certain conditions? Then you can use element transclusion to ensure that the DOM structure is only created and linked when you need it. Need some DOM content to be repeated times? Just use element transclusion, clone and append it the times. These two examples are over-simplifications of ng-if and ng-repeat respectively.

Such great fundamentals allow engineers to build complex things from simple pieces – the whole is greater than the sum of parts.

2. Test Driven Development (TDD) works great

This was my first project built from the scratch using  TDD and it was a pleasant experience.

The array of about 863 tests helped identify critical regressions very early. It gave me the freedom to rewrite sections whenever I disagreed with the style. And since the tests were always running (and very fast too, thanks Karma!); the feedback was immediate. Broken tests meant my ‘refactoring’ was actually a bug injection. I don’t even want to imagine what would have happened if those tests didn’t exist.

Guided by the book – a testament to Tero’s excellent work and commitment to detail – it was possible to build up the various components independently. The full integration only happened in the last chapter (for me, about 6 months later). And it ran beautifully on the first attempt! Well, all the tests were passing…

3. Easy to configure, easy to extend

This is a big lesson for me and something I’d like to replicate in more of my projects: software should be easy to configure and extend.

The Angular team put a lot of thought into making the framework easy to configure and extend. There are reasonable defaults for people who just want to use it out of the box but as expected, there would be people who want a bit more power and they can get desires met too.

  • The default digest cycle’s repeat count of 10 can be changed
  • The interpolation service allows you to change the expression symbols from their default {{ and }}
  • Interceptors and transform hooks exist in the http module
  • Lots of hooks for directives and components

4. Simplified tooling

I have used grunt and gulp extensively in the past however the book used npm in conjunction with browserify. The delivery pipeline was ultimately simpler and easier to manage.

If tools are complex, then when things go wrong (bound to happen on any reasonably large project), you’d have to spend a lot of time debugging or trying to figure out what went wrong.

And yes, npm is powerful enough.

5. Engineering tricks, styles and a deeper knowledge of Angular

Recursion

The compile file which would allow two functions to pass references to each other – an elegant way to handle state handovers while also allowing for recursive loops.

Functions to the extreme

  1. As reference values: The other insightful trick was using function objects to ensure reference value integrity. Create a function to use as the reference.
  2. As dictionaries: functions are objects after all and while it is unusual to use them as objects, there is nothing saying you can’t.

function a() {};

a.extraInfo = "extra"

Angular

Most of the component hooks will work for directives as well – in reality, components are just a special class of directives. So you can use the $onInit, $onDestroy and so on hooks. And that might even lead to better performance.

Issues

Tero did an awesome job writing the book – it is over a 1000 pages long! He really is a pro and knows Angular deeply; by the way, you should check out his blog for awesome deep dives.

My only issues had to do with issue resolution; there were a few issues with outdated dependencies but nothing too difficult. If he writes an Angular 2 book, I’d like to take a peek too.

Conclusion

I took a peek at the official AngularJS repository and was quite surprised by how familiar the structure was and how it was easy to follow along based on the concepts explained in the book.

I’ll rate the book about 3.9 / 5.0. A good read if you have the time, patience and curiosity to dive deep into the  Angular 1 framework. Alas Angular has moved on to 2 but Angular 1 is still around. Moreover, learning how software is built is a great exercise always.

Maturing as a software engineer


Looking back on my time as a developer, there are a lot of things I would have avoided doing if I had as much knowledge and maturity as I did now.

While I am grateful for the experiences and don’t regret them; I felt it would be a good idea to share these. These might motivate others or at least speed up their careers.

Here goes!

1. Patterns, patterns, patterns

When I take part in code reviews, I tend to look for recurring style patterns. Why? This helps to reduce the cognitive load on readers of the code (after all, code is written to be read).

I am  not advocating for bad software patterns rather having a plethora of ways for doing the same thing in a codebase creates confusion and productivity losses. How do you determine the ‘right’ pattern?

For example in JavaScript, there are several ways for creating an array.


var a = [];

var a = new Array();

var a = new Array(3);

Having a haphazard mixture only takes away brain processing cycles. Rather, have your team decide on a style and stick to it.

By the way, the first style is the ‘expected’ and preferred approach although there might be use cases for the latter two.

Ever wonder why the Google codebase is rated to be easy to work with? Well, think about consistency and established patterns.

2. Break the big picture down and make incremental progress

Building and distributing the smallest software piece you can imagine requires more effort than you would think. It is much more efficient to break down the big picture into small chunks of work that can be completed in an hour or less. Such breakdowns make you more effective and help in understanding progress and forecasting completion times (which is a tricky problem to solve).

I used to break down only the code pieces before (which itself was an improvement over my earlier dive-into-code-and-figure-it-out-as-you-go approach). Nowadays, I try to take some time and reflect on the end product itself: its behaviour, look and feel and how users would interact with it.

For a typical software project, such road maps covers:

  • Testing – unit tests, continuous integration,
  • Documentation – extensibility guides, tooling
  • Implementation
  • Discoverability and Distribution – release targets, getting started articles
  • Maintenance – handling bugs, user feedback etc

Sounds like too much work? Well, just focus on one small bit at a time and keep making progress.

3. Be lazy – start first on tasks with the largest impact/effort ratios

Two things matter: results and impact. There is no point in slaving for 20 hours to choose between blue and light blue if it has no impact on the users. Ditto for spending endless hours ‘arguing’ over what language should be used. Just choose the best usable one and deliver results.

My heuristic for tasks is thus:

  • Does completing the task move me closer towards the big picture?
  • Is this the easiest-to-achieve task with the biggest impact?

If so, I pick up that task and just do it – the goal is to maximize the impact/effort ratio.

Before I’d just stick to a task and spend endless hours on it even if it was something as trivial (and probably low-impact) as beautifying test scaffolding test output and elaborately designing test functions. Now? Common, my time is more valuable than that – I get the test functions right and try to get the coverage I want but won’t spend too much time once that is achieved and is readable for others.

Excessive polishing time can be spent on other more impactful pursuits like having fun with family or delivering high impact features.

4. Technical skills plateau

Sooner or later, you’ll get to the technical plateau. By that time, you’ll have so many successes under your belt and can detect potential pitfalls easily. Then, what next?

There are tons of ways to extend your impact and that is the way people become even better engineers. For example, I doubt if Anders Helsberg is still writing a lot of code, yet his ideas continue to empower and influence millions around the world.

Think about that, how do you scale your influence and make it possible to touch the lives of thousands of people? Are there engineering problems crippling your organization? Process pitfalls to improve with huge impact? Education ramp ups? There are always challenges to solve and problems to fix.

5. Choose career investments carefully

How would you set up an investment portfolio? Would you just go about investing in everything? Nope, you would evaluate the risks and benefits, consult experts and then invest in a select few areas while ignoring other areas.

You could spread out your risk by investing in a wide area but doing this excessively dilutes your returns. Conversely, investing in only company could be very risky too. Thus, it’s generally advised to spread  out your investment portfolio

Careers are investment portfolios. A typical career spans a long period ( upwards of 30 to 40 years) and shares some similarities with investments:

  • technologies, frameworks etc -> investment options
  • time -> funds

Just as you wouldn’t jump on every new fund, why would you do the same with your career? There is no harm in taking measured risks in careers but you should be strategic and know what your end goal is.

Every now and then a new framework pops up in the news. Before, I’d hop on the bandwagon and try figure it out. Nowadays? Well, if it really piques my interest, then I might spend some time learning about its core design principles and problem-solving approach.

If it neither solves any of my problems nor brings anything new to the table, then no thank you; I’d rather continue nurturing my current investment portfolio and hedging my bets.

Think about your bets and stick to them.

Conclusion

I am still learning and pray I continue. One thing that has struck me as being really critical is the will to try. We don’t know if something would work out or not however we can always try and then learn from the outcome (success or failure).

Don’t give up – continue learning and growing.

Defining JavaScript Functions


There are many ways to create functions in JavaScript; some styles are quite popular e.g. the function declaration and function expression while others are not.

//function samples
function declaration () {};
var funcExpression = function () {};
var namedFuncExpression = function named() {};
var fnConstructor = new Function ();

All approaches create a Function object however there are a couple of differences under the hood. Lets take a peek…

1. Function Declaration

The function declaration pattern is one of the most common patterns.


function fn () {
    //function body
}

Once a function is defined using the declaration format; it can be used anywhere in the enclosing scope even before the function body. Confusing? It’s all about hoisting.

2. Function Expressions

Functions are first class objects in JavaScript, thus it is possible to have expressions that evaluate to function values; such expressions are appropriately termed function expressions.

Creating a function expression is easy: just declare a function where the parser expects an expression and it automatically becomes a function expression. This includes assignments to variables and/or object properties, placing functions between parentheses (the grouping operator can only contain expressions),or after a unary operator (e.g. !, +, – etc).

//anonymous function expressions
var fnExpr = function () {};

(function fnExpr2() {} );

!function fnExpr3() {}

Function expressions can be named or anonymous; named function expressions (NFE) expose a function name that is available inside the function body but not outside it. See the snippet below:

var namedFuncExpression = function named() {
    return named.name;
};

named();
//ReferenceError, named is not defined

namedFuncExpression();
//returns "named"

NFEs help to make debugging nicer (we all know how frustrating it is to keep seeing ‘anonymous’ functions in the debugger). It might also come in handy when you need to retrieve a function name by calling toString() or make recursive functions (although the variable name will also serve the same purpose).

Function expressions are essential to achieve functional programming concepts like currying and partial application in JavaScript.

3. Function Constructor

The last way is to use the function constructor, this constructs a function object and returns it.

There are a few things to note while using this approach: created functions can only access variables in the global scope or in their own scope and it is no possible to create closures. Moreover, the use of the function constructor is slower since the function body has to be parsed every time it is called. And there are potential risks from malicious agents.

var add = new Function('a','b', 'return a + b');

add(1,2);
//3

var glbFoo = "global";
function scope() {
  var scpFoo = "scoped";,
   scop=Function('console.log(typeof scpFoo)'),
   glob=Function('console.log(typeof glbFoo)');

   scop();
   glob();
}

scope();
//logs undefined
//logs string

The parameters to the function are passed in first and then the function body (as a string) is passed in as the last parameter and viola! the function object is returned. The new keyword is optional; it still works without the new.

4. Function expression vs Function declaration

Function declarations and their bodies get hoisted to the top of the scope (no matter where they appear – top, bottom or middle) and are thus available through the scope. However for function expressions, the variable declaration is hoisted while the assignment to the function body only happens at runtime. See the example below:

declared();
//logs declared
function declared () {
    console.log('declared');
}

expressed();
//logs TypeError: undefined is not a function

var expressed = function () {
    console.log('expressed ');
}
expressed();
//logs expressed

5. Spot the difference? Identifying function expressions and declarations in code

Differentiating between function expressions and declarations can be tricky. According to the ECMAscript specification, function declarations must have an identifier. If functions without identifiers are unequivocally function expressions, how do we distinguish between a function declaration and a named function expression?

Function declarations are only allowed as sourceElements of scripts or functions. They cannot appear in nested blocks because blocks are  only allowed to contain statements and not sourceElements. Expressions on the other hand are expressions… :)

//source element examples
var a = 0;
function b () {};
if (false) {
    //nested, not source element
    var c = 0;
    function d () {};
}

//function declaration
function foo() {
    //another declaration
    function bar() {};

    if(true) {
        //function expression because
        //it is not a source element
        function baz() {};
    }

    //function expression
    //expressions expected after unary operator
    !function bng () {};
};

6. Finally…

Still reading? Here are some more examples of queer ways to create functions. Don’t do this at home/work/live/staging/etc.

var f = function (str) {
    alert(str);
};
eval('f("eval")');
//alerts eval

setTimeout('f("timeout")',1000);
//alerts timeout after a sec
};

Remember, don’t do this on live servers…

Did you enjoy this post? Here are a couple more interesting posts:

1. The JavaScript Function
2. Three Important JavaScript Concepts
3. A peek into JavaScript’s Array.prototype.map and jQuery.map

Update

Brilliant explanation of the difference between function and Function by Peter van der Zee:

“The `Function` is a global object and a function value, which generates a new function when called. You create new local variables named `Function` but not with `function` “

Three Important JavaScript Concepts


Statements and Expressions

The MDN documentation defines JavaScript expressions as any valid unit of code that resolves to a value.

a = 1;
3 + 4;

The first expression has value 1 which is then assigned to variable a while the second has value 7 which is not assigned to anything.

Since expressions evaluate to values (numbers, strings, functions etc), they can be substituted anywhere these values are expected. Programmers just have to ensure expressions resolve to expected values because of JavaScript’s weak typing.

Consider the following simple statement:

var x = "statement";

This is a statement and not an expression. Statements do something while expressions evaluate to values.

Dr. Axel has an excellent post which differentiates both: “you can use expressions where statements are expected (these are called expression statements) however you cannot use a statement where an expression (i.e. something that evaluates to a value) is expected”.

To simplify; you cannot use the statement above as a condition for an if branch (which expects an expression or value).

Variable Scope

Scope determines the lifetime of variables and where they can be used. Most programming languages have block scope, i.e. variables only exist within enclosing blocks (usually delimited by braces). However, JavaScript has other ideas… Check the code snippet below.

function JSBlockScope() {
    var noBlockScope = "BlockScope";
    if(true) {
        var noBlockScope = "FuncScope";
    }
    console.log(noBlockScope);
};

JSBlockScope();
//logs FuncScope

The output is “FuncScope”! Surprised? Block scope implies that declarations inside blocks do not overwrite external variables. JavaScript has function scope and consequently noBlockScope gets overwritten to ‘FuncScope’.

Function scope also means that variables declared inside functions are only available inside those functions, once the function exits, the variables are inaccessible anymore (well, unless you use closures…).

function scopeCheck() {
    var functionScope = "inside Func";
    console.log(functionScope);
};

scopeCheck();
//logs inside Func

console.log(functionScope);
//ReferenceError: functionScope is
// not defined

The functionScope variable defined inside the scopeCheck function only exists inside the body of the function.

Variables are first looked up in the current scope (i.e. the enclosing function), if not found, then the interpreter continues walking up enclosing scopes until it gets to the global scope. If the variable is not defined still; then the well-known “ReferenceError: variable is not defined” error occurs.

Hoisting

JavaScript has this weird but fascinating concept called hoisting. All variable declarations are hoisted – every variable gets declared immediately even though their values might not be set immediately.

function hoisting() {
    console.log(hoistedVar);
    var hoistedVar = "hoisted!";
    console.log(hoistedVar);
}

hoisting();
//logs undefined
//hoisted!

Explanation

Confused? Well what happens is that all declarations are hoisted to the top before any code execution begins. The above code is totally similar to the one below:

function hoisting() {
    var hoistedVar; //undefined
    console.log(hoistedVar);
    hoistedVar = "hoisted!";
    console.log(hoistedVar);
}

hoisting();
//logs undefined
//logs hoisted!

The log statements show ‘undefined’ since this is the default value for uninitialized JavaScript variables. If they weren’t hoisted, the interpreter would have thrown a ReferenceError. 

Hoisting also applies to functions (JavaScript functions are values afterall…) however there is a subtle difference between function declarations and expressions. Function declarations get hoisted to the top and are immediately available within the scope while function expressions are only declared and not hoisted (much like the same case for variables above). 

function isFunc(fn) {
    var isFn = (typeof fn === 'function');
    console.log(isFn);
}

function isUndef(fn) {
    var ntDef = (typeof fn === 'undefined');
    console.log(ntDef);
}

function funcHoisting() {
    isUndef(declaredFn); //false
    isUndef(hoistedFn);  //true

    function declaredFn () {};
    var hoistedFn = function () {};

    isFunc(declaredFn); //true
    isFunc(hoistedFn);  //true
}

funcHoisting();

Declaring variables at the top of functions helps to avoid hoisting surprises.

Conclusion

I was going to write about function expressions vs declarations, however I believe these three JavaScript concepts would significantly make it easier to understand that topic. Insha Allaah I’ll publish the function expression vs declaration post next.

Meanwhile, here are some other posts you might like:

1. The JavaScript Function
2. A peek into JavaScript’s Array.prototype.map and jQuery.map
3. Programming Language Type Systems I

Becoming a Professional Programmer


1. Deliver when you commit

It is extremely bad for your reputation to fail to meet up to your words; if you can’t deliver, please say no or find an alternative way out. How would you feel if an artisan disappoints you for no good reason? I bet you’ll probably never do business with them again.

So before you commit to anything (boring bug fixing, dreary testing or documentation), please make sure you’ll deliver or else do not commit.

2. Stay up to date

Do you know design patterns? Development methodologies? The latest fad in software development? Don’t neglect learning else you’ll wake up one day and realize that you are writing COBOL (OK, this is an exaggeration but you sure do get the hint). Staying up to date is your responsibility and no one else’s.

Dedicate time to make sure you know your field very well, ideally it should be time off work and gains should be measurable. While you are at it, please remember not to burn out, create time for fun things too.

Some employers actually make this easy by training employees or providing libraries – if you are lucky enough to have this, please take full advantage of this :); otherwise, there are tons of free stuff on the Internet that you can leverage – blogs, newsletters etc.

3. Be responsible

You must unconditionally know HOW and WHY your code works; and when testers find a bug, accept it as your doing graciouslyThis also applies to inherited code – no one wants to know if the code is spaghetti (with some onions and meat included) or written in Japanese. Once you inherit the code, it automatically becomes your code and it’s up to you to know how it works, what it does and how to fix/extend it.

If you are stuck with such bad code, why not try cleaning it up slowly? Every time you open the file, just make a tiny improvement (please no new bugs).

4. Know the domain

Most developers feel that users are not ‘smart’ enough to understand the intricacies of software development. Software development is complex but so are financial accounting, quantum physics , economics and a whole slew of other fields.

Imagine two developers working on a financial accounting product, the first writes software based on the requirements alone while the second makes a conscious effort to learn more about financial accounting. Who do you think will write better software and communicate more lucidly? Who will understand the problems, issues and challenges better?

Admittedly, it is more work trying to understand accounting (or any other field) but in the long run, it’ll pay off because you’ll write code in the problem domain. This also gives you the chance to contribute meaningfully to the project, proffer advice and analyze the competition. Who knows, it could open the door to new opportunities.

5. Work consciously

Most times we do things because we are accustomed to having things work that way – in coding, code design or requirements gathering. Ideally, we should always be thinking of our work – it should not just be the same ol’ thing all the time.

There are things to try, new (and probably better/more efficient) ways to write code and arcane language features to explore. Always keep asking these questions – why am I doing this? What am I doing now? Is there a better way to do this? It applies to everything – coding, design, maintenance and even meetings! Yes, meetings! :)

Did you enjoy this piece? Maybe you’ll also like becoming a better programmer and 5 things I learnt from Code Complete 2.

 

Taking the PAIN out of coding


Over the years, I have learnt some tricks and picked up some lessons while writing code. Most were learnt the hard way, so I decided to share a couple of tips on how to avoid development pitfalls.

Meticulous Planning and Design

One of the most difficult lessons I learnt in software development was not to rush into code; I used to jump impulsively into software projects and start hacking away at code without planning fully. And as you can bet, the thrill of coding soon evaporated when I got bogged down by messy code. Sadly, many projects of mine met their nemesis this way.

Now, I am just too lazy or maybe too battle-scared to do that; I mostly write out a high level system design document (usually a single page or two) describing all the major features. Next, I run through everything to know that the various components and interfaces are logically valid and try the edge cases. Only when I am satisfied with this do I start writing code.

Alhamdulilah, I think I write cleaner, more modular and better designed code this way. For example, I recently had to extend an experimental framework I wrote a couple of months back; surprisingly I was able to make all major changes in less than two hours. Better still, nothing broke when I ran the framework again!

A dev lead once told me coding is the easiest part of software development… I think I agree with him…

Do it fast and dirty, then clean up

I started using EmberJS last year for a web project. EmberJS is a really cool framework and reduces the amount of boilerplate code you have to write: it’s so powerful that some things seem magical. However, EmberJS has a really steep learning curve.

As usual, I was trying to write perfect code at my first attempt, did I succeed? Your guess is as good as mine. I got so frustrated that I started hating EmberJS, the project and everything remotely related to the project. :)

Before giving up, I decided to have one more go at it; my new approach involved ignoring all standards and good practices until I got something to work. And that was it, I soon got ‘something’ that looked like a working web application running. One day, while working on the ‘bad’ code, I had an epiphany. In a flash, I suddenly knew what I was doing wrong. Following standards and practices was relatively easy afterwards.

Looking back, I realize that if I was bent on doing it perfectly at the first go I most probably wouldn’t have gotten to this point. Oh by the way, EmberJS got a new release so my code is obsolete again. :P

Clean up the code from step 2 above X more times

This is a part of development I don’t think I really like but it is essential for maintenance. You have to go back through the code (yes, your code; you ain’t gonna make life miserable for the developer inheriting your codebase). Refactor all duplicated, extraneous and obscure pieces of code ruthlessly. Most importantly, improve the readability of the code (yes, readability is REALLY important – make it read like a good novel if possible à la Shakespeare or Dickens).

I also keep a running list of all the hacks I make as I go about writing code in step 2; this list comes in handy at this stage and enables me to go straight to the substandard code pieces and fix them up.

Use a consistent coding style

I recently noticed that my coding style was inconsistent across my projects: variables names were either under_score or camelCase while method declarations used brace-on-new-line and brace-on-same-line approaches.

The problem with this approach is that it breaks up my flow of thought and makes speed-reading code difficult. Now, I choose a single style and stick to it throughout a project – any style is fine provided I use it consistently.

Scientific Debugging

I came across the term ‘scientific debugging’ while blog-hopping and it has stuck in my subconsciousness ever since. Identifying bugs can be a challenge: for small projects, I just try to figure out where the bug might be and then check for this. However, this approach does not scale, I wouldn’t randomly guess on a 5000 line project.

Scientific debugging is a systematic process: you make hypotheses about the likely causes of the bug, make a list of places to check and then go through this systematically while eliminating entries. You’ll most probably find the bug with less effort and without running through the entire list.

Project Management

I rarely used to track how much time and effort I put into my projects; I would just code and code and code. Now I know better, I estimate how many hours I can put in before, during and after each project. I try to use Agile (although, I use a simple list + pomodoro too) for project planning, task management and effort estimation. It is now trivial looking up my project status: implemented features, issues list and proposed features.

Testing

I tried my hands at TDD last year and I felt it was just a way of doing too much work for coding. While I might be wrong about TDD, I think it’s essential to have a solid testing process in whatever project you’re doing.

Test, test and test: run the gamut (if possible): unit, integration, functional, stress, regression etc.

Enough said… I have dirty code to polish. If you did find some of the points useful, please share your thoughts and ideas.

Related Posts

  1. Symptoms of Software Rot
  2. So you want to become a better Programmer
  3. Clean code, Dirty code

Book Review: Coders At Work: Reflections on the Craft of Programming


I recently completed reading Coders at work (well, I must have spent approximately 8 weeks trying to complete it; not a record I am proud of but still it’s better than nothing!)

I think the book is a good read even though some of the interviews dragged on for too long. Peter Siebel, the author, does a good job of posing broadly similar questions to all the coders he interviewed. He nearly always asked each and every one of them about their coding techniques, most difficult bugs encountered, tools and debugging techniques, how to become a better programmer and how they learnt to program.

He interviewed 15 different programmers ranging from language designers and creators ( Brendan EichJavaScript, Joe Armstrong – Erlang, Joshua BlochJava platform features) to memcached creator (Brad Fitzpatrick, by the way, he wrote memcached at 23 :P). There are also a couple of computer scientists ( Donald Knuth and Peter Norvig) and others too.

Overall; I think the book is a good book and it enables you to see what coding was like back in the days before TDD, Agile, Kanban etc. I found some of the remarks delightfully hilarious and sincere. Most of these folks (including a single woman) were writing code on archaic (well; I bet they didn’t look archaic then) systems.

One unifying theme with all the coders is their love of simplicity and passion for programming. They’ve all spent lots of hours honing and improving their skills; some even know what the error codes of a language signify! Software development has changed a lot since then however the underlying concepts remain the same.

Here are a couple of quotes from the book:

Simple ain’t easy.  Joshua Bloch

A program is a black box. It has inputs and it has outputs. And there is a functional relationship between the inputs and the outputs.    Joe Armstrong

There is a Dijkstra quote about how you can’t prove by testing that a program is bug-free, you can only prove that you failed to find any bugs with your tests. Peter Siebel

Requirements always change… L Peter Deutsch

Programs are meant to be read… Code it as lucidly, as easy to read, as crystal-clear as you can. Do it the simple way.   Bernie Cosell

That’s about it; now go grab a copy if you’re interested.

Clean Code, Dirty Code


Phew! It feels like forever since I wrote a blog post; I have been extremely busy – finished an internship and had to travel back to school. Well, I actually have a couple of drafts but didn’t get to publishing them. Am I too lazy? Na you sabi… :).

I don’t know why I seem to keep talking about code and JavaScript nowadays, I just can’t seem to stop; probably because I think JavaScript is the next BIG thing; what other language is currently experiencing rapid widespread adoption on all major platforms? Read this  and this if you want to know more.

Back to clean code; clean code is what we developers should aspire to write, it is clean, minimal, simple, easy to understand, free of extraneous details, it is simply BEAUTIFUL (there I go again… drooling about software right?). Ideally you shouldn’t be able to remove pieces of it without breaking the system. Less code is always better, the less code you have to write; the better your system. Jeff Atwood actually says the best solution is no code at all.

So what makes code dirty? Anything that makes it a pain to modify, work with or extend. Abstruse code, duplicated logic, global state and variables, tightly-coupled dependencies, contorted flow (I mean those convoluted loops and if constructs), wrongly-exposed variables (it-shouldn’t-be-there-but-I-can’t-help-it-syndrome) etc. In fact, if your codebase doesn’t read easily then you should get into it and clean it up. It’ll go a long long way in saving you painful work in the future. I have worked on a multi-thousand line codebase (> 25k lines and written by lots of people) and found it easy to handle and understand; in contrast I also inherited some smaller codebase (< 4k lines and written by a single person) and it was a pain to extend.

How to get there? Do Code reviews, relentlessly refactor and make sure you don’t leave anything to chance; you have to be professional about your job and code.

Here is an example in pseudocode:

function showMessage(message){
{
    if(message != null){
        show(message);
    }
    else{
        show(defaultMessage); //This is the default action to do
    }
}

Alright; lets clean this up

function showMessage(message){
{
    if(message == null){
        message = defaultMessage;
    }
    show(message);
}

Isn’t the second version just cleaner and easier to use? Here is another example:

    if(condition)
    {
        //do Something
        alertUser();     
    }
    else{
        //do Some other thing
        alertUser();
    }

The alertUser() function is getting called in both conditional branches so it can be moved out. Less code, simpler code, easier to read. Here is the cleaner version:

    if(condition)
    {
        //do Something
    }
    else{
        //do Some other thing
    }
    alertUser();

I feel that writing clean code is even more important than solving the problem you set out to solve. Most of our time is spent extending code, writing fixes and maintaining existing code. Keeping it simple and nice at the start will go a long way in making your job and mine easier.

Levels of Developer Expertise


This is a light-hearted attempt to classify developers. Enjoy…

 1. Newbie

  • Knows the syntax of one language and thinks he’s a master.
  • He might also think the language is the best one in the whole wide world.
  • Full of passion and excitement about programming.
  • Writes code that ‘seems’ to work – it compiles after all – doesn’t necessarily know why it works though.
  • Uses bad variable names.
  • Programs are likely to be be a single file.
  • Doesn’t know anything about testing.
  • Likely languages: PHP or Java.
  • Likely Reads: Learn Java in 21 days or something similar.

 2. Amateur

  • Learns another programming paradigm/language.
  • Uses better variable names – must have been hurt by evil names.
  • Has developed one or two relatively decent working systems.
  • Writes spaghetti code.
  • Re-invents the wheel.
  • Probably has a huge ego.
  • Only knows how to use IDEs.
  • Tests? They are just a waste of time and delay the release of his ‘one-of-a-kind’ software.
  • Still dives into code without proper planning.
  • Likely Reads: Advanced Java.

 3. Intermediate

  • Becomes language agnostic.
  • Knows multiple languages, tools and paradigms – their strengths, flaws and best uses.
  • Has worked on bigger projects – understands the importance of design and flexible code.
  • A bit of spaghetti code here and there but he tries to avoid this.
  • Understands algorithms and data structures.
  • Can use text editors.
  • Uses version control.
  • Designs code before starting and tries to avoid code duplication.
  • Probably has his own blog or contributes to open source projects.
  • Likely Reads: Pragmatic Programmer.

 4. Advanced Intermediate

  • Codes defensively – using asserts, unit tests and checks.
  • Really cares about his code; refactors aggressively until it is clean.
  • Toying with building his own language or at least understands how languages work.
  • Can ‘smell’ bad code.
  • Interested in reuse and sees patterns in software engineering.
  • Understands design patterns.
  • Knows about methodologies like Agile, Scrum, Kanban.
  • Likely Reads : The Mythical man-month, Clean Code, Code Complete, the Java language specifications.

I don’t know much about master programmers – am not one yet though I pray I do get there.

I am curious about what you think, let me know in the comments.