The art of coding: writing code for humans, not machines

memoizr
4 min readJul 5, 2023

--

It is a common misconception that we write code for machines. We don’t. We never did. We do and always have written code for other people. To solve their problems, whatever they might be. We write code with other people, for other people.

Our Tools

We do this using tools. Like any good craftsman we adapt to the tools we use to become good at solving people’s problems. We do this so well that we often end up doing more and more work for the tool itself, and less and less work to solve people’s problems. You see, the problem is that urge to master and adapt to a tool we have. We got it the wrong way around. The tool should adapt to us. The way we work, the way we read, the way we write. Ergonomics matter. Arguably that is the single biggest innovation of ChatGPT. They came up with one of the most ergonomic ways of interacting with their model: natural language.

About Kotlin

To me, one of the biggest draws to Kotlin isn’t the data classes, coroutines, when syntax or any of these individual features Java will be reimplementing over the next few decades. You see, Java with sealed classes, records and so on is still and will always be Java. Sure many developers with Java shaped calluses won't have an issue with that. They are used to write code for Java, and they're happy with the pay they receive for it. To me the biggest draw to Kotlin is that it encourages you to write code for people. For people who will have to read it, extend it, keep it alive. For people who will not curse your legacy.

But don’t get me wrong, I’m not saying Kotlin itself makes you write better code. Sure it’s neat using Java 25 features since 2018, but you’re still writing code for machines. Kotlin allows you and encourages you to write code for humans first. But you have to use Kotlin as Kotlin, not as a Java 34 replacement. Sure your Spring Boot code might lose a few lines here and there, but we all have had that chat with that team member who says: “yeah cool but why not stick to Java until Java 42 with Project Gollum? It’s the same thing”. Those colleagues have a point. Project Gollum and Project Nirvana will deliver better ways of doing Functional Programming and Object-Oriented as soon as Java 48 drops next Tuesday, but you know what, that’s already old news.

Where to next?

Programming languages are distributed in levels. Assembly can be regarded as the lowest, and human natural language, the highest. For every language we developed paradigms. With Java and others OO became popular. With Scala and Kotlin some FP concepts went mainstream. The trend is that of an obvious progression to ever-increasing levels of abstraction. With ChatGPT and other models we’re now witnessing a new paradigm come up, people working on it call themselves “Prompt Engineers” I believe. So we have these paradigms, Imperative, Object-Oriented, Function-Oriented. Arguably the next one is Human-Oriented Programming. Maybe we will all be developing in a HO style in the future. I’m not sure about the name, but I think “HO” might be sticky. Who knows, maybe it will get to the stage of seeing HOs absolutely everywhere, like in Vegas.

ChatGPT HO style seems to be the way things are going. That may be the endpoint, or perhaps that will be supplanted by Nerualink-enabled stuff. But we’re not there yet. ChatGPT is not precise enough to let us write complex and robust programs. The tool isn’t ready, but what if we, for now, just took the style?

But how?

I’ve been toying with this idea for years at this point, and I wrote Snitch to satisfy my curiosity about HO. How can we handle a fairly rigid aspect of development, like developing at the HTTP layer in a way that’s as human as possible? So this is what Snitch looks like:

withTransaction {
POST() with body<CreateUserRequest>() isHandledBy createUser
POST("login") with body<LoginRequest>() isHandledBy userLogin
userId / "posts" / {
authenticated {
GET() onlyIf principalEquals(userId) isHandledBy getPosts
POST() onlyIf principalEquals(userId) with body<CreatePostRequest>() isHandledBy createPostGET(postId) isHandledBy getPost
PUT(postId) with body<UpdatePostRequest>() onlyIf principalEquals(userId) isHandledBy updatePost
DELETE(postId) onlyif (principalEquals(userId) or hasAdminRole) isHandledBy deletePost
}
}
}

I won’t pretend that this is by itself anything groundbreaking. Many other frameworks can do something very similar. But pay attention to the detail. Just looking at the code you can tell that endpoints declared inside the withTransaction block will have a transaction. That some endpoints, but not others are authenticated and that certain endpoints have checks that executed onlyIf some conditions are met. It's not what Snitch does that's different, is how it does it and, more importantly, that it does it with explicit intent. There are precedents to this, we have come across Gherkin for example, and we know Ruby and RoR did great work to improve readability. Http4k is actually pretty good in this regard, and you can achieve remarkable stuff with it. But why stop there?

Closing thoughts

Human language is great, and ChatGPT is extremely ergonomic, but as any prompt engineer will be able to confirm, human language is also frustratingly vague if you want to get to some exact behaviour, every time. We want to write code for humans, collaborating with other humans, and it seems we want to do it in an as-human-as possible way, because, you know, we’re human. But we do have to be precise and exact as we interact with machines and dictate their behaviour. It may be worth seeing what this HO style can or should look like in practice. With Kotlin and Snitch I’ve explored this topic to see where it leads. Most likely, any such approach is at best a stopgap until ChatGPT-like approaches become more refined and exact. But maybe ChatGPT will get stuck at a local minimum, and will not be able to fully supplement programming languages and frameworks. In that case, maybe we’ll be better off with something that moves further in that direction, while still being somewhat rooted in some battle-tested legacy programming concepts. Now what do you think about this?

--

--