Christian Heilmann

Author Archive

Lines of code – how to not measure code quality and developer efficiency

Tuesday, February 6th, 2024

Lately I have a person who pings me daily asking how many lines of code I have written today. The first time was on the weekend. My answer was “none, as weekends I concentrate on not doing computer things with my partner” which is an excellent idea.

However, lines of code are often seen as productivity or even quality of a developer. And that’s nonsense.

When Elon Musk took over Twitter he asked people to print out (!) the code they’ve done in the last few days to prove their worth. This lead to a lot of ridicule, and well deserved. This isn’t the 60ies.

This is Margaret Hamilton, lead software engineer of the Apollo Project. Here (left) she stands next to the code she wrote by hand that what used to take humanity to the moon. [1969]

When pressure rises in companies you often have to prove your worth as a developer. You also have to prove your department’s worth as a lead developer. And as lines of code are easy to measure, this is often the hammer used to hit the nail in the coffin of company trust.

The amount of lines of code is a weird thing to measure as there are several things that interfere with it:

  • If you look for a lot of code and equal that as efficiency, the code might actually be bloated nonsense to hit a goal of “x lines of code”
  • If you look for a small amount of code, it might be overly terse or use a lot of dependencies. I can write 5 lines of code that do the job, but under the hood include 3 libraries of 2MB each
  • We live in a time of preprocessors, bundlers, compilers and conversion for almost every code we write. So the original count of lines is pretty pointless. How much code gets shipped to the end user, is, of course, another story.

The amount of code necessary depends on a few things that can’t be measured in lines. Much like you can’t value the quality of an essay or book by the amount of words.

Code should first and foremost do the job it is written for. The outcome should be a product for the intended audience. For end users this means an app they use without any security and privacy issues that’s easy to use. For other developers, it’s similar. The code should play nice with other code, not have any performance or security issues and be easy to include. Often this means concentrating on the documentation, rather than the code itself.

So why do people care about the amount of code written? Well, it could be concerns about your developers not being seen as doing enough. It could also be a worry that your product has become bloated and hard to maintain. But often I think it goes back to finding one way to measure things. One way we learned in school. When students are asked to write an essay, the demand comes with a challenge of “at least x words”. That’s why. This has entered meme territory years ago.

Meme about replacing the word therefore with for this reason the conclusion can be drawn that there must be a direct, and obvious, connection between the two ideas so

An equivalent in code would be: “write a function that checks if a number is between 1 and 10”.

A pretty decent solution is:

function isOneToTen(number) { 
  return number > 0 && number < 10 
}

If LOC is your end goal, just do:

function isOneToTen(number) { 
    if (number === 1) {
        return true;
    }
    if (number === 2) {
        return true;
    }
    if (number === 3) {
        return true;
    }
    if (number === 4) {
        return true;
    }
    if (number === 5) {
        return true;
    }
    if (number === 6) {
        return true;
    }
    if (number === 7) {
        return true;
    }
    if (number === 8) {
        return true;
    }
    if (number === 9) {
        return true;
    }
    return false;
}

Both work, and you could argue that the longer one allows you to add more use cases. You could, for example, react differently to different numbers. However, we all can agree that this is not efficient code when it comes to writing and reading it as a human.

Interestingly enough, code like this can often perform really well. I remember working in very limited environments, like 8 bit computers. And there writing more often meant better performance as the code didn’t need expensive computation. These days are over though, and often micro-optimisations like these cost more time than they are worth.

What about keeping things as short as possible? Often you have competitions that measure the cleverness of code and skill of the coder by the terseness of their code. The first money I made with code was winning a competition in the “64er” computer magazine for a 20 line of code BASIC program that was a text editor in 1990. You also see one-liner contests and the like. Is smaller code always a sign of quality? I’ll write about this next time.

For now, please stop seeing lines of code as proof of worth for engineers. It just shows that you have no idea how building products with software works. Often the best code is the one that didn’t need to be written. Writing code is one task and doesn’t need to be the start. A much better investment is thinking about the product and what you want to achieve in the amount of time you have. Code can adapt, so can developers. Think about the needs of your users and validate features before creating them. The amount of code is only important when it becomes a performance or maintainability issue.

Using details/summary and colour coding on GitHub pages

Wednesday, January 24th, 2024

As CODE100 is coming to Amsterdam, we needed an archive of all the code puzzles we had so far. As the challenges are all on GitHub, it made sense to use GitHub pages for that. So I needed to fix two issues:

  • I wanted to have colour coding of code examples much like the GitHub own templates have.
  • I wanted to have collapsible and expandable parts of the page. I did not want to write an own JavaScript solution for that.

Here is how to achieve both. You can check the CODE100 puzzle archive to see this in action.

Adding source code colour coding to GitHub pages

In your `config.yml` file define kramdown as the markdown used and rouge as the highlighter.

markdown: kramdown
highlighter: rouge

This allows you to use code fences and get colour coding. You can add your own CSS to colour it any way you like.

Adding collapsible elements using detail and summary

The new(ish) HTML elements detail and summary result in collapsible document sections. The great thing about these is that they don’t need any script and work independent of input. Even better is that using in-page search also automatically expands sections. At least in Chromium based browsers. The problem is that there is no way in markdown to define these sections.

GitHub allows to use summary and detail as HTML inside their flavour of markdown. If you check the notworking.md file in the source repo, you can see it working.

Collapsing and expanding in markdown on GitHub

However, if you render it as GitHub Pages the content inside the `details` stays markdown and doesn’t get rendered (even when you add newlines). You can of course use HTML (as the second demo shows), but this defeats the purpose of using markdown.

Collapsing and expanding in GitHub Pages not working

My workaround was to use HTML comments and create an include to use in my page templates.

HTML comments are great because they don’t do anything in GitHub markdown. There seems to be no standard for Markdown comments, after all.

The `detail-summary.html` include is as simple as it gets:

{% capture summary %}<!-- summary -->{% endcapture%}
{% capture details %}<!-- details -->{% endcapture%}
{% capture endsummary %}<!-- endsummary -->{% endcapture%}
{% capture enddetails %}<!-- enddetails -->{% endcapture%}
{% assign newhtml = include.html |
 replace: summary, '<summary>' |
 replace: endsummary, '</summary>' |
 replace: details, '<details>' |
 replace: enddetails, '</details>'
%}
 
{{ newhtml }}

In my page template I need to use this as a pre-render instead of simply using `{{ content }}`:

{% include detail-summary.html html=content %}

And in my markdown files I use HTML comments:

<!-- details -->
<!-- summary -->
## Solution
<!-- endsummary -->
 
``json
 
 [
   0,0,0,121,231,143,195,118,216,
   195,54,223,195,182,216,121,231,
   143,0,0,0,3,15,30,97,155,183,
   49,153,179,1,157,187,3,207,30,
   0,0,0
 ]
``
 
Did you get it? Did you find a better way?
<!-- enddetails -->

This renders as a collapsed section with `Solution` as the summary.

Demopage in the browser

The nice thing here is that it enhances progressively. In the GitHub rendered readme it is just a headline.

Github rendered markdown of the file

If you want to play with this, I created a bare-bones version here.

10 print chr$(205.5 + rnd(1));:goto 10 in JavaScript

Friday, January 19th, 2024

Forget about the Vision Pro, or whatever Samsung just brought out, we all know that the pinnacle of computing fun has been released in 1982 in the form of the Commodore 64.

One of the coolest things you could show people when writing BASIC on it was the following one-liner:

10 print chr$(205.5 + rnd(1));:goto 10

Executed, this resulted in a diagonal endless maze:

What the one liner does is print a character with the PETSCII code 205 or 206 (or SHIFT + M and SHIFT + N) which are fat diagonal lines. It does that using the PRINT command and the CHR$() command which turns a number into a character, much like fromCharCode() does in JavaScript. Luckily enough, the CHR$() command doesn’t care if it gets integers or floats. without the CHR$() it would be a list of floats:

When you ended a PRINT command with a semicolon, the computer didn’t add a new line but kept the cursor where it was.

Michel de Bree also has a version of this that is even shorter using Assembler and the D012 functionality, which stores the current scanline of the screen. He also pointed out that there is a whole book about this one liner and others that use faux random designs on the good old breadbox.

Now, let’s try something similar in JavaScript. A classic approach would be nested for loops.

let out = '';
for (let y = 0; y < 10; y++) {
    for (let x = 0; x < 40; x++) {
        out += (Math.random() > 0.5 ? '\\' : '/');
    }
    out = out + '\n';
}
console.log(out);

This works, but doesn’t look good.

Maze generated with slash and backslash

The reason is that slash and backslash have too many pixels around them. UTF-8 has box drawing characters, which allows us to use two diagonals that have less whitespace, β•² and β•± respectively, or 2571 and 2572 in unicode.

Using this, and moving from classic nested loops to chained array methods, we can do the following:

console.log(new Array(400).fill().map((_, i) => 
    (i % 40 === 0 ? '\n' : '') + 
    (Math.random() > 0.5 ? '\u2571' : '\u2572')
).join(''));

We create a new array of 400 items, fill it with undefined and map each item. As the item is irrelevant, we use _, but what’s important is the index, so we send this one as i. We then add a linebreak on every 40th character or an empty string. We then use Math.random() and see if it is above 0.5 and add either β•² or β•±. We join the array to a string and log it out.

This looks better:

Maze generated with unicode characters

However, it doesn’t have the WUT factor the original one liner had. Luckily enough the two unicode characters are also following one another, so we can use fromCharCode with Math.random() to do the same. JS is not as forgiving as BASIC on Commodore64, so we need to round to the next integer and as we use unicode, fromCharCode() also needs a ‘0x’ to work:

console.log(new Array(400).fill().map((_, i) => 
    (i % 40 === 0 ? '\n' : '') + 
    String.fromCharCode('0x' + '257' + 
    Math.round(Math.random() + 1))
).join(''));

Coding is fun. Let’s do more of it without reason.

Want to do it live with a chance to get to the finals of the CODE100 coding competition? We run another edition of it in Amsterdam on the 29th of February. Apply here as a challenger!

Rob Bateman and his journey to resurrect Flash content on today’s web

Thursday, December 21st, 2023

Flash has been a boon to the web and the bane of my work as a web standards advocate. Now defunct, it is great that people work on converting old content. I’ve been talking to Rob Bateman about his work on converting Flash games to run on today’s web in an edition of Coffee With Developers.

I met Rob again at Halfstack in London and if you are interested in more details about the work he is doing, his “An Emulator’s journey” is also available on YouTube:

A santa themed CODE100 puzzle – Hitting the chimney

Tuesday, December 19th, 2023

Illustration of Santa facepalming because he failed to hit the chimney with his presents

I just finished another puzzle for the CODE100 competition and thought I make it Santa themed. Imagine Santa cocking things up by dropping his presents everywhere but inside the chimney. Can you find out which ones hit, which ones landed on the chimney and which ones dropped outside of it?

Ilustration showing points in the circle as grey, points outside the circle as black and points on the circle as green

You get points in a coordinate system that are the drop points of presents and you should sort them into different arrays: those that landed in the chimney, those that landed outside and those that landed on the chimney.

You get the a JSON dataset of a coordinate system with a certain height and with and a circle in its centre with a radius of 75 pixels that is 10 pixels wide.

The `droppoints` array contains the coordinates of the dropped presents, and your task is to sort them into different arrays. Copy all points that are inside the circle into the `innerPoints` array, all the ones outside the circle into the `outerPoints` array and all that landed on the chimney into the `onChimneyPoints` array. Store the size of each of the arrays in `inside`, `outside` and `chimnney` and return all of them as a JSON object.

For example, the original dataset is:

{
    "width": 300,
    "height": 300,
    "chimneyWidth": 10,
    "chimneyRadius": 75,
    "inside": 0,
    "outside": 0,
    "chimney": 0,
    "innerPoints": [],
    "outerPoints": [],
    "onChimneyPoints": [],
    "droppoints": [[127,37],[202,112],[113,84], … ] 
}

Your result should be something like:

{
    "inside": 30,
    "outside": 120,
    "chimney": 4,
    "innerPoints": [[157, 170],[169,170],[131,166], … ],
    "outerPoints": [[90,279], [16,247], [242,140],[72,209], … ],
    "onChimneyPoints": [[208,102] [208,102] … ],
}

I’m sad to report that with this dataset, `20` presents landed on the chimney.

Good luck! You can submit your solution in this Gist on GitHub. If you use JavaScript to solve the puzzle, here’s a CodePen that can get you started.