When is it a Good Idea to write Bad Code?

Imagine an asteroid is barreling towards earth and the head of NASA tells you and your development team that they have 12 months to code the guidance system for the missile that will knock the asteroid off it’s earthbound trajectory.

Your first thought may be, “Sh$@*t!”  But let’s say you agree.  What does the quality of your code look like after 12 months?  Is it perfect?

Now imagine an asteroid is barreling towards earth and you have only 1 month to code the guidance system.  Now, imagine a worse scenario, you only have 24 hours to code the guidance system or the earth is a goner.

What is Technical Debt?

You should read Martin Folwer’s excellent introduction to technical debt.  Technical debt could be defined as the difference between the application that you would like to build and the application that you actually build.

All software projects with limited resources will accumulate technical debt.  

Technical debt is often viewed as a negative or pejorative term.  However, in reality, technical debt can be leveraged to actually launch successful applications and features.  The key to leveraging technical debt is to understand:

  • What types of technical debt can you afford to accumulate?
  • Where in your codebase can you afford to accumulate technical debt?
  • When do you need to make a payment on your technical debt (when to refactor)?

Application development typically involves a variety of groups and individuals, for example the development team, business stakeholders, and your end users.  To understand technical debt, it’s important to realize the following:

Technical debts are always paid by someone.

Types of Technical Debt

To understand technical debt, it is important to understand the different types of technical debt that can accumulate in an application.

Missing Functionality

Missing functionality can make the user experience of your application more awkward to use, but on the up side it doesn’t pollute the codebase.  Chopping features can be a useful technique for reaching deadlines and launching your application.

Tradeoffs

  • User experience can suffer from missing functionality.
  • Overall utility of your product or service may be compromised.
  • Future development will be more costly, if the feature must be added later.

Broken Functionality

Broken functionality refers to code that “straight up” does not work under some or all circumstances.

Tradeoffs

  • Possible increased risk of catastrophe (system failure, security compromise, data corruption, etc.)  Sometimes code can cause serious damage to people and systems.
  • User experience may suffer as users run into bugs that inhibit their ability to use your application.
  • Utility of your application can decrease significantly.
  • Plan on adding the broken functionality to your bug list, so future development will suffer.

Bad Architecture Design

Systems benefit from being loosely coupled, and highly independent.  Conversely, highly coupled systems with lots of dependencies or interdependencies can be highly erratic and difficult to maintain and upgrade.  They can also be very difficult to reason about.

Bad architecture decisions can be some of the most expensive to fix, as architecture is the core underpinning of your application.

Tradeoffs

  • Certain solutions may not scale with traffic based on their initial design or could be cost prohibitive to effectively scale in the future.
  • Logical complexities, for instance in the case of a poorly designed database, can ripple through the entire application.  Making future development slow to a crawl.

Bad User Experience Design

If you’re users don’t know how to user your application or make mistakes while using your application because of an overly complex user interface, than it will surely be your user base that is paying down this form technical debt.

Bad user experience design can be very expensive to fix since the whole of the application development process depends on it.  Leaving the very real possibility that you’ve spent your limited development resources creating subsystems and code that don’t properly solve the problems of your end users.

Tradeoffs

  • Decreased utility as your users struggle to fully take advantage of the application.
  • Very complex refactorings, since user interface inherently tend to be highly coupled, so again more possible drag on future development.

Lack of Testing

The main purpose of testing, both automated and manual is to answer the simple question, does your code work?  If you can’t answer this question, then you’d be better off heading to the casino rather than launching your application.

In a lot of ways, “lack of testing”, can be viewed through the lens of understanding what you’re application is capable of.

Tradeoff

  • Maintaining the quality of an application is not possible if you can’t answer the “does it work?” question effectively and efficiently.
  • Future development can be more difficult without a way to tell when functionality stops working or when new features break old ones.

Bad Code Readability

This is usually referred to as code smell, and again I like Martin Fowler’s explanation.  Bad code readability is easy for a developer to spot, because they simply look at the code and decide if it makes sense to them or not.  Therefore it can also be subjective.

Being able to read and understand code is critical to being able to debug and modify it later.  This should not be confused with broken functionality, which is a different concept.

In developer circles, there is a somewhat innate bias towards overemphasizing this form of technical debt (the reason being that it is the developer, that will ultimately pay for it).  Just remember that you can have “Bad Code” that doesn’t smell (silent but deadly).

Tradeoffs

  • Future development can become much more difficult, if not impossible (rewriting working code is common if the readability is bad enough)
  • Risk of being highly dependent on the original developer/team for information about the system (see the bus factor)
  • Loss of the ability to understand your entire application, what it does and how it works.

All applications will have some level of technical debt in each of the above categories, but again, it is in managing this debt that gives a development team the ability to successfully launch their application.

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Brad Carleton

Brad is a rockin' fun JavaScript coder who enjoys building cool stuff and long walks on the beach. As the Founder/CTO at TechPines, he has worked with large companies and startups to build cutting-edge applications based on HTML5 and Node.js. He is the creator of Bone.io, a realtime HTML5 framework, and is the author of Embracing Disruption: A Cloud Revolution Manifesto.
This entry was posted in project management, technical debt and tagged . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • zihotki

    Bookmarked! Thanks for such an awesome write up.

  • Gillian

    Does this work on Linux?

  • http://www.gunnarlium.com/ Gunnar Lium

    I would argue that missing and broken functionality isn’t really technical debt, but rather incomplete work. Techinical debt is something you have when the system works, but it has characteristics that make changing, maintaining and scaling hard. Not sure I would consider bad UX technical debt either.

    • techpines

      It certainly depends on your definition of Technical Debt.

      I think it’s useful to think of Technical Debt as the collection of technical tradeoffs taken to complete a project within a limited time frame with limited resources. In this light, missing functionality and broken functionality fit into that definition as they factor directly into the overall value of the finished application, and they are technical tradeoffs.

      Bad UX Design can lead to brittle user workflows, and un-intuitive user interfaces. Both of which are usually built with lots of frontend code, that is rarely very reusable. Therefore, UX re-designs are going to result in lots of code rewriting. (Then again maybe you don’t view good UX as the development team’s responsibility.)

      • http://www.gunnarlium.com/ Gunnar Lium

        My question is whether not implementing a feature is really a technical decision. The use a metaphor, say you’re bulding a house and you run out of cash before installing the windows, you can either A) hold off on buying windows until you’ve earned enough money, or B) take a loan and buy the windows now. In example A, you would miss a (criticial) feature, but you won’t have any economic debt.

        Whether good UX design is the development team’s responsibility is also a bit beside the point. I don’t consider everything a development has to fix to be technical debt. If anything, a lacking UX can be considered design debt. If the what’s missing is the proper design, it’s not technical debt, even though it will be implemented with code.

        But as you say, it depends on definitions. I find it helpful to consider technical debt as something that has no immediate consequence for how a product works “right now”, but that wil impede further development or scaling.

        I believe that by narrowing the focus, it becomes easier to apply solutions, and to identify what’s really technical debt.

  • João P. Bragança

    Seems like the right time to write bad code is for a flashy demo. Otherwise, why?

    Impending doom is where you cannot get it wrong. There is a huge gap between 5 billion people will die and customers did not get their email.

    • techpines

      A flashy demo is a great example of leveraging technical debt to achieve a short-term goal (in this case showing off your demo). You might take on technical debt in Code Readability, Architecture Design, Lack of Testing, Missing/Broken Functionality, but you probably don’t skimp on your UX Design. This gives you the ability to sell your product before most of it is even built.

      But most applications follow some sort of path:

      Proof of Concept => Demo => Prototype => Internal Users Only => Alpha Release => Beta Release => Mature Software? (continue iterating)

      Therefore, I think there is a pretty broad spectrum between a demo, and something larger and more mature, where making technical tradeoffs can be leveraged so that you can ship functionality.