What is technical debt?

Technical debt is the total amount of less-than-perfect design and implementation decisions in your project.

– The Art of Agile development

There are many forms technical debt can take:

  • hard-coded logic for one-off customer requests
  • test suites that take too long to run
  • front-end builds that take too long to transpile
  • incomplete test coverage
  • design choices that once reflected how the business operated but no longer does
  • a design/pattern that seemed like a clean way to implement something at first, but doesn’t hold up over time

You can find it in those methods that are dozens of lines long and doing too much. It’s in those functions that have unintelligible conditional statements. It’s in the parts of code that are hard to change because they break other places. It’s in the functions that were too hard to test so they never got tests.

How do you get in tech debt?

The first way, and the kind that you have the most control over, is choosing to implement or fix something the quick way instead of the right way. This can happen, for example, if a bug has gone unnoticed for months and then a customer suddenly finds it, causing them to lose a lot of money. The bug becomes an urgent matter and you end up fixing it the quick way. Then, you put an item in the icebox to re-design that part of the code the “correct” way later on.

Another way this kind of debt happens, is when you hack together a feature to “get it working”. You do another pass or two over it to clean it up a little. Since it “already works” and would involve a lot of effort to refactor it into something more re-usable/sustainable, it just becomes a permanent part of the code base.

The other kind of technical debt is from inheriting legacy code. You know, the first team wrote it as a prototype and took every short cut imaginable in order to meet deadlines. Now you’re trying to incrementally make it better. The first team made tons of design decisions based on how the product worked in the beginning. Now, hundreds of iterations later, the app works completely differently, yet the core of the app still has these original design decisions baked into its skeleton.

What are the side effects of technical debt?

Even following best practices, code will accumulate technical debt over time. When the original design or pattern used for a certain feature gets stretched to the max and contorted into something not resembling the original idea, that part of the code becomes a bug breeding ground.

According to [Boehm]361-730-0617, about 20 percent of the modules in a program are typically responsible for about 80 percent of the errors. It seems like technical debt tends to congregate in specific parts of the system.

Overall, technical debt leads to higher maintenance costs. Even simple things that should take minutes can take an entire afternoon. But the ultimate worst case scenario is the application becomes more expensive to change than to just re-write it from scratch.

Strategies to reduce debt

In a codebase that you inherited, toward the beginning, you may be able to get a decent velocity out of sheer sweat and tears. Unless the codebase was in great shape, that velocity is unsustainable and will probably go down pretty steadily because things become harder and harder to change. Not only do you have the debt from the previous team to deal with, but now you’re adding technical debt just to meet deadlines.

One long-term strategy to reduce debt is to set aside “slack” in the velocity each iteration. The Art of Agile Development book I quoted earlier says that a good rule of thumb is to spend 10 percent of the iteration on technical debt. This will obviously decrease velocity in the short term (they say even for as long as a quarter or more). But as debt gets paid down, productivity and velocity will eventually be higher than it was before. This is because code changes will be easier to make after the code is cleaned up.

Another thing you can do every day, is to be generous with your refactoring. If you’re working in a part of the code base that is particularly difficult to get around in, find small ways to improve it while you’re there. Rename that variable that confuses you. Move that unrelated code into the right place. Add a documentation comment here and there. Complete the test coverage of the function you’re working in.

Now there’s a fine line between refactors that are sensible to do in the given story you’re working on and ones that are too big. You’ll find the right balance between what you get overwhelmed with and what is doable for you. I will say that it is all too easy to be scared of change and just do the bare minimum that the story requires. That is fine, especially for junior developers, but this won’t help on the technical debt front. If you don’t leave the code cleaner and easier to change than when you found it, your debt stays the same.

Prevent debt

One way to avoid technical debt is to avoid shortcuts. The trick here is learning when to push back against your product team or whoever is making this an urgent matter. Learn to recognize situations when the team is considering implementing a feature or bug fix the quick way. This might be because you’re trying to get it out of the way so you can get back the planned set of features  that need to get done for the deadline.

This is easier said than done, but use simple designs. The Zen of Python says if it’s hard to explain, its a bad idea. There’s a lot of truth to that. If a design in hard to follow and hard to extend, it’s just asking for someone to misunderstand it and let a bug slip in.

Another preventative measure is to refactor as you go. Get used to the ivy bush work flow. Write code to make a test pass, then immediately clean it up and make it better. This is such a hard discipline to master, and the topic could fill an encyclopedia, but it’s worth mentioning here.

Agile frameworks like XP have been designed with technical debt in mind. Look at any agile framework and you’ll see a good set of practices you can start with that will go a long way toward keeping your technical debt low. Technical debt will always creep in, but a good set of agile practices will help you effectively mitigate and reduce it as you go.

(919) 824-9628

The Installation Guide to Follow

I think it’s important to have a local python environment running on your actual computer. Some beginning tutorials will have you type code into their integrated environment and it will execute the code for you (Treehouse, Codecademy, etc). That’s totally fine for getting you familiar with the basics of programming and the language. But real programming on anything substantial will have to be built in a real development environment.

For python installations, I always follow this amazing guide: (434) 282-9751.  Click the link to whichever operating system you’re on and follow the instructions. 

Invoking the Correct Python Installation

Python has 2 major versions: 2 and 3. A lot of operating systems still come with python 2 as the default python interpreter. If you open a terminal and type  python, and hit enter, you’ll probably see something like this:

Python 2.7.10 (default, Oct  6 2017, 22:29:07)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

You can exit this python interpreter  by typing exit().

Notice it says Python 2.7.10. If that’s the case (or any version of 2.x.x ), anytime you use the name python to execute a script, you’ll be invoking Python 2.x (legacy python). The guide linked above, gets you up and running with the name python pointing to Python 3.x for Mac and Windows. But for Linux, you’ll still have to use the python3 name. The python3 name will also invoke Python 3 on Mac and windows. I personally, prefer to be explicit with the versioned name unless I know 100% that it points to the right python.

So, that’s kind of the over-explained scoop on knowing how to  invoke the right Python interpreter. Python also has a package manager for Python libraries called pip and it is just as confusing to know which one you’re installing/invoking.

Using Pip and Invoking the Correct One

You’ll need pip to install packages like django, requests, flask etc. The python guide linked above will walk you through getting it set up the right way for your operating system.

For Linux, he recommends following the official pip installation instructions. If you’re following this guide, you may be new to Linux as well and not really understand the guide. Basically, they want you to download a script from the command line with this command:

curl /bootstrap.pypa.io/get-pip.py -o get-pip.py

So cd into your home directory and run that command. The curl program will save that get-pip.py script to your home directory. Then they want you to execute that script with python. Here is the part that can trip people up: make sure you’re using the python3 installation to execute it like this

python3 get-pip.py

Depending on which OS you’re running, you may still have to use the pip3 name to invoke the pip associated with python3.

If all that isn’t confusing enough for you, most python developers use vitualenv to make an isolated python environment for each project they work on.

Virtual Environments

Now that you have pip installed, you can use it to install virtualenv with

pip3 install virtualenv

That will install virtualenv and give you access to the virtualenv command. Don’t type this yet, but you create virtual environments with a command like virtualenv <my_project_name>. It will install whatever is the default python interpreter for your environment unless you explicitly tell it which python to use e.g. virtualenv my_new_project --python=python3

When you create a new virtual environment, a new folder with the name you gave the virtualenv command is created with a complete python installation. You can “activate” the environment which basically temporarily makes the python and pip keywords refer to the associated virtual environment in the current terminal session. You can also just directly use the pip and python executables in the virutalenv folder they were created in.

I’ve worked with people who like to keep their virtualenv folders in their project folder, but a good practice is to keep all your virtualenvs in a single folder away from your projects. Another package called virtualenvwrapper helps organize and manage all your virtualenvs, but I’ll let you set that up if you’re interested. There are ports of virtualenvwrapper for Windows, but you have to either install it for Powershell or cmd specifically.

Make a folder for your virtualenvs and create one. Here is what to do for mac and linux (the $  is just the terminal prompt. No need to type that):

$ mkdir ~/virtualenvs
$ cd ~/virtualenvs
$ virtualenv --python=python3 getting_started 
$ source getting_started/bin/activate

Here is what to do for windows (The > is the windows terminal prompt. No need to type that):

> mkdir C:\\path\to\home\folder\virtualenvs 
> cd C:\\path\to\home\folder\virtualenvs  
> virtualenv --python=python3 getting_started 
> C:\\path\to\home\folder\virtualenvs\Scripts\activate

You’ll notice your prompt has the environment name in parenthesis like (getting_started) ~ $ Now you can install things into your environment with commands like pip install django. When you’re ready to get out of this environment, simply type the deactivate command and you’ll notice the environment name in parenthesis is gone which means the virtualenv is no longer activated.


Those are just some things I remember getting confused and stuck on when I was getting started. I hope that my over-explaining is helpful to people who are also confused. Let me know if there are other areas of the install/set up process that were bumpy for you.