How I Program in 2024
Kartik Agaram reflects on his programming journey, advocating for minimalist software design, emphasizing simplicity, context awareness, and the potential benefits of data-oriented design to improve software quality and adaptability.
Read original articleIn 2024, Kartik Agaram reflects on his programming journey, emphasizing the importance of building trustworthy software infrastructure. He discusses his experiences with the Mu platform and Freewheeling Apps, noting a shift in his approach to programming over the years. Initially, he prioritized tests and version control, believing they were essential for quality software. However, after a month of reworking his text editor, he deleted all tests and found that this radical change led to a better program. Agaram concludes that building durable software for a broad audience is challenging and suggests focusing on simpler, less commercialized software with fewer dependencies. He highlights the impact of context on software design and the tendency of existing software to become overly complex due to short-term incentives. Agaram advocates for a minimalist approach to programming tools, warning against over-reliance on abstractions and tests, which can complicate software unnecessarily. He proposes that when understanding of a program's context stabilizes, it may be beneficial to discard large portions of the code and start anew. He also introduces the concept of data-oriented design as a valuable perspective for improving software. Agaram acknowledges that his programming has evolved since his last reflections in 2019 and expresses curiosity about future developments in his approach. Overall, he emphasizes the need for thoughtful programming practices that prioritize clarity and adaptability over complexity.
Related
Programmers Should Never Trust Anyone, Not Even Themselves
Programmers are warned to stay cautious and skeptical in software development. Abstractions simplify but can fail, requiring verification and testing to mitigate risks and improve coding reliability and skills.
Why We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools and reducing complexity enhances reliability. Prioritizing simplicity over unnecessary features offers better value and reliability.
Htmx: Simplicity in an Age of Complicated Solutions
Erik Heemskerk discusses the pursuit of a 'silver bullet' technology in software development, emphasizing simplicity over complexity. He critiques over-engineering in front-end development, highlighting trade-offs in code solutions for better user experiences.
My programming beliefs as of July 2024
Evan Hahn emphasizes tailored programming approaches, distinguishing "simple" from "easy," promoting testability through modularity, and advocating for ethical coding practices prioritizing societal impact and nuanced thinking in software development.
Fear of over-engineering has killed engineering altogether
The article critiques the tech industry's focus on speed over engineering rigor, advocating for "Napkin Math" and Fermi problems to improve decision-making and project outcomes through basic calculations.
- Many commenters emphasize the importance of testing and version control, arguing that they are essential for maintaining software quality and collaboration.
- Some express skepticism about the author's approach, suggesting that it may work for individual projects but is not scalable for team environments.
- There is a recognition of the trade-offs between simplicity and the potential risks of not using established practices like testing.
- Several comments highlight the subjective nature of programming practices, with some supporting experimentation while others advocate for traditional methods.
- Common frustrations with testing and version control are noted, indicating that while they can be burdensome, they ultimately serve important functions in software development.
Never have I tested anything and NOT found a bug, and most things I tested I thought were already OK to ship.
Thus, when you delete your tests, the only person you are fooling is probably yourself :(
From reading your page I get the impression you are more burnt out from variation/configuration management which is completely relatable… I am too. This is a hard problem. But user volume is required to make $$. If the problem was easy then the market would be saturated with one size fits all solutions for everything.
I can’t understand how anyone would willingly program without using source code control in 2024. Even on a single-person project, the ability to work on multiple machines, view history, rollback, branch, etc. is extremely valuable, and costs almost nothing.
Maybe I’m misunderstanding what the author means by “versions”?
This workflow works very well for the author. Most of us can probably think of a time when Git or automated tests frustrated us or made us less productive. There are similar solutions that are simpler and get out of the way, e.g. backing up code with Dropbox, FTP, whatever.
The above is works well because the author is optimizing for their productivity on a passion project where they collaborate with few others.
Automated tests are useful, but it sounds like the author likes creating programs so small that the value might not surface. I think that automated tests still have value even in this context, but I think we can all agree that automated tests slow you down (though many would argue that you see eventual returns).
Version control and automated tests solve real problems. It would be insane to start a project without VC today, and automated tests are a best practice for a reason. But, for the authors particular use case, this sounds reasonable.
---
Aside from the controversial bits around VC/tests, I think items 7/8/9 perfectly capture my mindset when writing/refactoring a large program. Write, throw it away, write again.
I really wonder what about it made it be upvoted to first place.
This is a primary motivation for having a reasonable test suite - limiting frustration. Test suites gives developers confidence to evolve a system. When done properly, contributors often form an opinion similar to:
> But I struggled to find ways to test the rest, and also found I was getting by fine anyway.
This is also a common situation. As functional complexity increases, the difficulty to test components or the system as a whole can become prohibitive.
> Now it's 2024, and a month ago I deleted all my tests. ... In effect I stopped thinking about version control. Giving up tests and versions, I ended up with a much better program.
This philosophy does not scale beyond one person and said person having recent, intimate, memory of all decisions encoded in source code (current or historical). Furthermore, given intimate implementation knowledge, verifying any change by definition must be performed manually.
It started with an unconventional UI with a home page listing email accounts and for each account the number of unread and total messages. There was a unified inbox but it was not forced on users.
I remember that I explicitly selected this app because it fit my needs: one personal account, one work account, several work accounts that my customers gave me. I wanted those account to stay separated.
Probably a lot of K9 users picked that app precisely for the same reason because there were many complaints when the developer migrated to a conventional Android UI with a list of accounts sliding from the left and an extra tap to move from an account to another. If we had liked that kind of UI chances are that we won't have picked K9 to start with.
So one small change (but probably a lot of coding) destroyed the fitness of the app to its users. I keep using the old 5.600 version, the latest with the old UI, and I sideload it to any new device I buy.
Furthermore, to make things even more unusual, I only use POP3 to access my accounts (I preview on phone, delete stuff, possibly reply BCCing myself, eventually download on my laptop) and K9 fit perfectly that workflow. I don't need anything fancy. An app from the 90's would be good enough for me.
One thing is clear to me though, creating (software) by yourself is a completely different activity from doing it in a team.
About testing. Tests are means, not ends. What we're looking for is confidence I think. So when I feel confident about an implementation, I'll test less. And if I desperately need to make sure something keeps working, I'll add a few integration tests at the outer edges that are not so affected by refactorings and thus won't slow me down as much. E.g poking a web backend from the outside, as opposed to testing the internals. Unit tests are good for fleshing out the design of new API's, but those tests are pretty much useless once you know where you're going.
Great quote! You can even replace “software” with “businesses” and the quote still works.
It's a shame that there are so many rude comments. It seems like there are many close minded folks lurking here, forgetting that experimentation is essential in tech.
"Small changes in context (people/places/features you want to support) often radically change how well a program fits its context. Our dominant milieu of short-termism doesn't prepare us for this fact."
My opinion here is that short-termism is precisely a consequence of the hardness of predicting/keeping up with these small changes: businesses prefer to be able to adapt quickly to new scenarios rather than risking being stuck in the wrong direction.
This kinda beggars belief for me. I wonder who these people are - do they have the "battle scars" from working on complex or big systems? Are they reasonably junior or new to the profession with less than 10 years experience?
Next up? Fuck structural engineers, it's just going to slow us down building this bridge...
If you are doing something for fun, sure do whatever you want. I write zero tests for my own pet projects. But please in professional environments please don't ignore hard-won lessons in reliability and engineering-velocity because you don't want to have to do the extra work to update your tests. Your customers and colleagues (potentially years in the future) will thank you.
I do sometimes code in this pattern. I have high confidence in charts from Google and Akamai about some data I have exposure to (a variant of the inputs unique to my situation not in their hands) and when the curves I make conform in general trend to the ones they make over the time series, I am pretty sure I have this right. If the critique is in the fine differences I do some differential on it. If the critique is in the overall shape of the curve, if mine is like theirs, why do you think I am so wrong?
The best code I've seen mercilessly elides anything that doesn't serve an architectural level, problem domain-relevant concern. GADTs and hash tables, and all our nice CS tools work much better when applied as cognitive tools in a domain-specific manner as opposed to reified language syntax or library APIs, as the latter necessarily introduces cross-domain concerns and commensurate incidental complexity.
The most blatant example of this in APL is using arrays and tables for everything. Trees? Can be efficiently encoded as arrays. Hash tables? Same. Tuples? Just a pair of vectors. Etc. APL's syntax really shines in this instance, since data interaction patterns become short, pithy APL expressions instead of zoos of library functions. Using direct expressions makes specialization much easier, by simply ignoring irrelevant concerns.
Anyway, APL aside, I'd really like to see our software engineering zeitgeist move more toward an optimistic refining our understanding of the human practice of software engineering and away from pessimistic and programming-centric problem avoidance.
(The above really came out more treatisy than intended. Oh well.)
If the Primeagen has helped popularize the term again, great, thank you.
I don’t typically write tests, but they do make sense for a few cases (specifically end to end tests that look for well defined outputs from well defined inputs). I was inspired by Andreas Kling’s method of testing Ladybird, where he would find visual bugs, recreate the bug in a minimum reproducible example, fix the bug, then codify the example into a test and make sure the regression was captured in his test suite[0]. This led to a seemingly large suite of tests that enabled him to continue modifying the browser without fear of regressing somewhere.
I used this method of testing while I was writing a code highlighter that used TextMate grammars. Since TextMate grammars have a well defined output for some input of code + grammar, I was able to mimic that output in my own code highlighter and then compare it to TextMate’s output for testing purposes. I wrote a bunch of general purpose tests, then ran into a bunch of bugs where I would have mismatched output. As I fixed those bugs, I would add the examples to my test suite.
Anyways, my code highlighter was slow, and I wanted to re-architect it to speed it up. I was able to completely change the way it worked with complete confidence. I had broken tests for a while in the middle of the refactor, but eventually I finished the refactor. As I started to fix the broken tests, there was a domino effect. I only had to fix a few tests and that ended up automatically correcting the rest. Now, I have a fast code highlighter and confidence that it’s at least bug for bug parity with the slow version :)
This is one of those sentences that is clearly an opinion but stated as if it were some undeniable, incontrovertibly true statement of fact.
In your opinion, you have a better program - but give the code or repository to another dev or a group of devs and I'm sure you'll hear very different things...
For a one man show - go on do not write tests, especially if you do not know where you will end up with the software. But in teams I find a lot of value in (good written) tests, preventing bugs and documenting bugs. Sure you can over-engineer it, as everything else too.
BUT working without version control? Good that it works for you. I think version control is one of the MUST USE tools.
Wikipedia on Dubar:
> A replication of Dunbar's analysis on updated complementary datasets using different comparative phylogenetic methods yielded wildly different numbers. Bayesian and generalized least-squares phylogenetic methods generated approximations of average group sizes between 69–109 and 16–42, respectively. However, enormous 95% confidence intervals (4–520 and 2–336, respectively) implied that specifying any one number is futile.
I'm wondering what would work better? Writing a small program from scratch, using a rich ecosystem of libraries, seems like one way to go. AI help makes that easier.
Another approach, more common with popular apps that have more to lose, is to evolve in place.
He happily and condescendingly told every else how much they sucked because he had 1000% test coverage
When he released he had tons of bugs because his code wasn’t going what it was supposed to
His answer: yelling at product and tech leads for not being clear
The rest of us had tests but spent as much time asking clarifying questions
The guy above is one of the reasons I just lost all interest in software. This was a major FAANG company and his smooth talking continues today with management none the wiser because “he has the tests” Arby’s
For me, ChatGPT saved a lot of my mental load. I don't think about individual lines NEARLY as much. Obviously you need to understand what the program is doing and be smart about it, but you can really focus on business problems.
It spits out something like 40% of my code and 70% of tests. I've started dropping whole files into it and tell it how to combine a new code.
How did people program in Lisp for decades? I like types and such, and have even gone so far as to write Python like it's Rust. But in the end I realized dynamic languages have an appeal for a reason, and by using types all over the place, I was not getting the benefits of a dynamic language like Python.
When context is mostly static, dynamic languages shine. Context could be, for example, the structure of the directory. If I want to read a file and I know that the file exists, throwing a bunch of type checks about file reading operation is just overkill and slows down the development.
Sadly, the AI isn't capable of generating working code for all scenarios, so eventually, I take over the code and finish/fix it.
The workflow itself can be quite frustrating (who doesn't love fixing bugs in other people's code?), and the act of coding isn't as much fun as it used to be, but the AI also shows me new algorithms, which is a great way of learning new things.
Let's just say I am looking forward to 2025 ;-)
Related
Programmers Should Never Trust Anyone, Not Even Themselves
Programmers are warned to stay cautious and skeptical in software development. Abstractions simplify but can fail, requiring verification and testing to mitigate risks and improve coding reliability and skills.
Why We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools and reducing complexity enhances reliability. Prioritizing simplicity over unnecessary features offers better value and reliability.
Htmx: Simplicity in an Age of Complicated Solutions
Erik Heemskerk discusses the pursuit of a 'silver bullet' technology in software development, emphasizing simplicity over complexity. He critiques over-engineering in front-end development, highlighting trade-offs in code solutions for better user experiences.
My programming beliefs as of July 2024
Evan Hahn emphasizes tailored programming approaches, distinguishing "simple" from "easy," promoting testability through modularity, and advocating for ethical coding practices prioritizing societal impact and nuanced thinking in software development.
Fear of over-engineering has killed engineering altogether
The article critiques the tech industry's focus on speed over engineering rigor, advocating for "Napkin Math" and Fermi problems to improve decision-making and project outcomes through basic calculations.