When I open my social media feeds, it is obvious: the age of writing code with our fingers is ending. LLMs and agents can write code much faster.
But was it ever about the speed of source code creation? Was writing code only translating requirements into something the computer can understand?
I think not.
While I generate code with LLMs and agents, creating a piece of code by hand has its benefits. So, this is an ode to “slowly” handcrafted code.
Context matters
What I describe in this post does not apply to all coding. LLMs are good at writing glue code, mapping data from one format to another, repetitive stuff, and more.
The application I’m working on for over ten years – an attendance time tracking system with duty planning, activity time tracking, expenses and more – does, however, have a lot of complicated business logic with computations that depend heavily on configurable business rules.
I’ll talk about developing this business logic-heavy code here – please keep that in mind.
What I compare here
The hype is clearly spec-driven development using agents. We (developers + domain experts) iteratively update the specification, let the agents update the code, review the changes, and correct or update the specification; then let the agents update the code again, review, and so on. This is by no means a waterfall; it is highly iterative.
For our business logic, I prefer another approach:
First, I discuss with a domain expert what needs to change or be added. Then I jump directly into code. I use LLMs as code-completion (or function-completion) helpers, and ask them a question or two from time to time. This results in my typing roughly 50% of the code and the LLM completing the other 50%.
The nasty details matter
Whether writing a specification or writing code directly, we need to think about the problem and possible solutions. While the specification is quickly happy, the code is very pedantic – especially because we use F# as our programming language. This pedantry makes me think about every detail; I can’t just ignore – or overlook – these small details. A specification for complicated business rules quickly becomes full of holes. Abstract prose text just does not give me the “tools” to think through the details. When a function returns a Result, I have to think about both the happy and the error cases. I can’t forget the error case.
Over my years working in the software industry, I’ve seen many specifications. Good ones and bad ones. They all had in common that they missed some details that were later found while coding. With an agent, the agent very likely will fill this hole with whatever it thinks is most likely.
So, the slower process of typing code gives me time to think through all these little details while I create the software. With agents, I’ll probably stumble over these details while testing the application. Or worse, a user finds them – aka bugs.
I think we can quickly fall under the wrong impression that we get speed from LLMs and agents, but most of this speed is actually just postponing part of the work to later. And you probably know the saying about finding problems later and how fixing them later costs way more.
Writing code is faster
F# is a wonderful language for writing complicated business logic. Its type system (records, discriminated unions, and units of measure) and language features (pattern matching, active patterns, pipes, computation expressions) enable concise, easy-to-read, easy-to-understand code.
For me, it’s just the perfect language for business logic-heavy applications. The code clearly shows the business intent with minimal clutter. Some code reads like a specification.
The advantage of code is that it is non-ambiguous and deterministic. There is no interpretation.
A specification written in plain English (or any other natural language) is full of ambiguity. And we all interpret what we read. To explain something at a level close to code typically leads to very detailed specifications.
As a result, my experience is that I’m faster writing business logic directly than going through a specification.
Maybe you object: “But the specification can be used in the communication with my stakeholders. It has additional value worth the time writing.”
I’m really happy for you if you have stakeholders who take the time to understand your specification. Our stakeholders already struggle with the much-simplified user manual. A domain expert could understand the specification, but not the other stakeholders. So, for us, a specification is not a good way of communication.
Context switching or busyness vs efficiency
Finally, agents also need time to implement the specification and tokens, lots of tokens for complicated code. This switching between writing specifications or prompts and waiting for the results introduces a lot of context switching into my workflow. Context switching is probably the number one efficiency killer. Even worse, we feel very busy.
Maybe, reflect in a calm moment about business versus efficiency. I can’t tell what your situation is. But in my situation, I prefer writing code a bit slower, but without context switches. I’m done earlier this way. This depends on context, of course.
Conclusions
The idea for this post crossed my mind during my mountain bike tour this afternoon (cold but sunny). So there might be holes in my thinking; let me know about your experience using agents and LLMs. Just one request: before writing a comment, please reflect on your situation first, then write me your context, and how you evaluate your efficiency regarding hand-written vs generated code, and how context switching affects you.
Appendix
The following code shows a typical command implementation in our system. The command creates a manual activity, like “I worked on <undertaking> from 10:00 till 11:00 by doing <action>”.

46: parallelAyncResult states that this block might fail with an error, and that it uses parallel asynchronicity
47-49: Load the structure (defines permissions) and return an error when it can’t be found
50-52: Load undertaking (defines additional permissions) and return an error when it can’t be found
54: Validate that the current user is allowed to write this manual activity
55: Validate that the activity can be booked on this workday
56: Validate that the action exists on this undertaking and can be booked (it might be marked as finished already, or something else is wrong).
58: We use event sourcing, so we create the event data here (okay, that is a bit verbose and not spec-like)
73-77: We tell the OperationRunner to log the command for auditing, persist and log the event, execute all needed side-effects because this workday changed for the employee, and, finally, to mark the command as successful. In case something goes wrong, the command is compensated to restore the previous state.
A specification would not be shorter and probably less clear. That’s why I keep coding by hand.
[…] An ode to “Slowly” handcrafted code (Urs Enzler) […]