Category Archives: Software Development

Applying my first two programming lessons to teaching kids to program

I have been meeting with my 10 year old nephew about once a week to make video games together. It’s mostly just to have fun and for me to also chat with his dad, who helps on his side—we’re across the country from each other. His dad and I went to middle school together and got kicked out of wood shop and into a computer “shop” where we learned to program on the Commodore PET. We were 13 at the time and learned enough BASIC to make simple games. So, in a way, we’re carrying on that tradition.

My first program back then was to draw on the screen given a list of coordinates. It was about 5 lines of real code (and a dozen lines of DATA). It was easy to understand and it put stuff on the screen right away, which was my first lesson in programming.

It’s hard to do that in modern programming, especially if you want to be able to keep going on to build something more real. There’s just so much boilerplate and ceremony to just get something on the screen.

After some trial and error, I have settled on using Phaser as our game engine and Glitch as our editor.

Glitch lets you edit code like a google doc. We can both be in the editor and change code simultaneously. Ironically, it took me time to remember Glitch even though I got to witness its genesis inside of Fogcreek when I was working at Trello.

Glitch is easiest to use with JavaScript, so I had to pick a JS game engine. Since someone already had made a remixable Phaser game, I started with that. My second lesson in programming is that it’s easier to learn how to modify a working thing than to make something from scratch. This is really an extension of the first lesson—when you are making something bigger, it takes too long to get something working, so to learn how to make big things, start with the big thing already done. First you modify parts to get a sense of all the pieces, and then later, you will be able to make one from scratch.

I’ll post some of our games next week. They are mostly simple ports of classic 2D games from the 80’s and 90’s.

Mutation Testing

A couple of years ago, I wrote about a testing technique that I had learned, but didn’t remember the name of, so I called it code perturbance. I mentioned this technique in my book, and a helpful beta reader let me know the real name: mutation testing.

The idea is to intentionally change the code in a way that introduces a bug, but is still syntactically correct (so you can run it). Then, you run your test suite to see if it can find the problem. This augments code coverage, which will only let you know that code was run in a test, not if an assertion was made against it.

Now that I know the name, I can find out more about it in google. For example, there are tools that can do it for you automatically. The one that I’m most interested in is Stryker-mutator, because it supports TypeScript. I’ll report back when I try it.

Add Date Tests for Daylight Savings

I wrote my iOS App, Habits, after I had been at a job with very strict daylight savings coding rules, so it has lots of unit tests that take the code across the daylight savings/standard time boundaries.

But now it’s been almost 20 years (!) since I worked there, and I’ve gotten bad habits, which bit me last week. On Nov 3rd, at 2am, and for the next week, I had a bug in my code for calculating the date that was “one week ago” because I used JavaScript’s setDate() instead of setUTCDate().

Luckily, my app is still being written, not in production, so it was only a problem in my unit tests. The tests use the current date, and so they inefficiently find daylight savings problems.

The right way is to also have tests that specifically set the date to known daylight savings boundaries and the dates around them.

Technical Debt Typology Research Paper

A few months ago, I got an email from Mark Greville that included a link to a research paper he coauthored, called A Triple Bottom-line Typology of Technical Debt: Supporting Decision-Making in Cross-Functional Teams.

In the paper, the authors identify several categories of tech debt. One category is internal vs. external effects. In my book, I also identified the external category, which I call visibility. The paper thinks of the entire business as the “internal”, but I think of the team itself as the internal part. My separation is driven by difference in communication that the engineering team needs to use for itself vs. the rest of the business. Customers and other public stakeholders would likely be similar to the non-engineering business teams.

Since a lot of my book is about how tech debt affects developer productivity, I break down internal to the various ways it could reduce productivity. I use misalignment to describe tech debt that doesn’t meet the documented standards of the team. When the code is hard to change, for example if there’s messy code all over the codebase (Marbleized Code Fat), I call that resistance. If the code does what it’s supposed to (so no external effects) and the customers highly depend on its behavior, I warn about the risk of regressions.

Another pair they describe is whether the tech debt is taken knowingly or unknowingly. This is useful from a taxonomy perspective and might contribute to tech debt avoidance, but in my book, I write:

I don’t think of tech debt as the result of an intentional shortcut borrowed from the future. Some debt starts that way, but the reality is that lots of tech debt happens because the world changes. Even if your system represents your best ideas of how to solve the problem at hand, your ideas will get better, and the problems will change. You can do everything right and still have bad code, so it doesn’t help to judge the decisions that got us there. Learn from them, but it’s counter-productive to dwell on them.

My chapters on these dimensions focus on using them to decide what to do about the debt, and I don’t think intention is a factor in deciding what to do next.

The paper is worth a look and also has quite a good bibliography if you are interested in research on tech debt. Since the methodology of the research included a literature review, the list of references reviewed is another treasure trove of research.

Adam Tornhill on Tech Debt’s Multiple Dimensions

In the research for my book on technical debt, I ran into this talk by Adam Tornhill:

Adam has a similar perspective to mine: technical debt is multi-faceted, and the right strategy should address its various dimensions.

One of his examples is combining a code complexity metric with data from your source code repository to define low code health hotspots—areas where code is both complex and frequently changed. To find the hotspots, he built a tool to calculate this metric and visualize it. In the video he shows data from big open-source projects (like Android and .NET core) and pinpoints areas that would benefit from work to pay down debt.

Similarly, in my book, I identify eight dimensions of debt. Complexity is something I consider to be part of Resistance, which is how hard or risky it is to change the code. I would also incorporate low test coverage into resistance, as well as subjective criteria. Adam says that complexity is a good estimate of how many tests you need, which is true, and I give you credit for having the tests. I am mostly concerned by complex code that is undertested.

Like Adam, I believe that bad code only matters if you plan to change it. He believes that the repository history of changes is a good indication of future change, which I agree with, but to a lesser degree. In my book, I recommend that you look at the history and shared this git log one-liner as a starting point:

git log --pretty=format: --name-only --since=3.months | sort | uniq -c | sort -rg | head -10

That line will show you the most edited files. To find the most edited folders, I use this

git log --pretty=format: --name-only --since=3.months | sed -e 's/^/\//' -e 's/[^\/]*$//'  | sort|uniq -c|sort -rg|head -10

This data contributes to a dimension I call volatility. It’s meant to be forward looking, so I would mostly base it on your near future roadmap. However, it is probably true that it is correlated with the recent past. In my case, this data is misleading because I just did a reorganization of my code to pull out a shared library to share between the web and mobile versions of my app. But, knowing this, I could modify the time period or perhaps check various time periods to see if there’s some stable pattern.

Generally, my opinions about tech debt and prioritization are very aligned with what’s in this video, especially the multi-faceted approach.

Marbleized Code Fat?

I go back and forth on whether the name “Tech Debt” is the most useful term. In my book, I decided I can’t fight the term, so I use it, but in my opening paragraphs I make an argument that the problem we call tech debt isn’t like other debt.

The most common way I’ve seen tech debt is that it’s just everywhere. It’s not limited to a specific old module, it pervades the codebase like marbleized fat in a good steak, but not as delicious.

My Guest Article on Tech Debt for the Pragmatic Engineer

I started writing a book on Tech Debt in January and I posted a few chapters in the Spring. A few months later, Gergely Orosz from the Pragmatic Engineer reached out to ask me to write a guest post for The Pragmatic Engineer newsletter—it just went up today.

https://newsletter.pragmaticengineer.com/p/paying-down-tech-debt

If you want updates about the book, sign up here: Swimming in Tech Debt

There are a lot of posts on this site about tech debt. Here are a few

Policies that reduce dependencies

Over time I have become skeptical of most dependencies. I wrote in Third-party Dependencies are Inherently Technical Debt:

[…] any third-party dependency is technical debt. Third-party? Here’s how I am defining it for the purposes of this article. You are the first party, the platform you are writing on (e.g. iOS, Android, React, NodeJS) is the second party. Everyone else is third party.

[Third-party dependencies are debt because] you need to constantly update them, […] they introduce breaking changes, […] they become unsupported, […] your platform adds their own incompatible implementation, [… and] they don’t update on the same schedule as the [platform].

To that end, I like policies that tend to reduce the number of dependencies you have. Here are a couple that I have seen work.

  1. Become a committer on any third-party dependency you take on. To be fair, you kind of owe that to the project.
  2. Donate to any third-party dependency you take on where you won’t become a committer.
  3. Fork the dependency and bring in updates carefully.

Seems like extra work, right? The extra work is why they work.

PM-led vs. Engineering-led Time

At Trello, on the mobile team, we had a formal way of allocating engineering time to either working on PM-led or Engineering-led initiatives.

A PM-led initiative was something that ultimately rolled up to a OKR that was well-understood by the business. The requirements came from the PM and the PM could assess acceptability. An Engineering-led initiative was something like tech debt, changing our dependency package manager, improving CI build times or anything where the requirements came from the team itself and the PM didn’t know or care about it (and neither did anyone outside the team).

So, let’s say we decided the split was 60% on product manager led initiatives and 40% on what we called engineering-led. The split is arbitrary—the EM and PM agreed on what was appropriate for their team and set it for the year.

Then, any individual engineer at any one time (for a couple of sprints) was on either a PM-led project or an Engineering-led project. We did not want a single engineer to be split across PM/Eng-led work. This made it easy to know we were allocating correctly (without having to track time on individual stories or cases).

So, if it was 60/40 and we had 10 engineers, 6 would be on PM-led, and 4 would be on engineering-led at any one time, but it rotated.

This just needed to be mostly right over the course of a year—on any specific month it could be a little off if over the long-term, it matched.  For example, If the PM didn’t have work ready, we could do more engineering-led work temporarily.