David Heinemeier Hansson (DHH), creator of Ruby on Rails, writes about programming philosophy, testing, business and technology politics.
HN commenters cite his essays on Rails as omakase and test-induced design damage. They quote his strong criticism of test-first dogma.
> You can't refactor code without tests
See this is where the conversation goes south. You will use the phrase "unit tests" when it suits you, and then, like a magician using indirection, generalize to the word "tests" when it suits you.
Why are you listening to Uncle Bob so much?
I mean you can do as you like. However, the strong "Uncle Bob" style assertions should probably be left out of most articles in favor of a more humble "this is what worked for me/us" approach. Universal rules for any discipline are very hard to come by. Sharing what worked for you on your projects is great but it becomes somewhat obnoxious when you try to generalize it to universal rules that works in all environments for everyone.
Also, I think that large unit test suites, where most of the tests are redundant, come about in competitive environments where if you try to make a commit without a test some other competitive coder will try to use it as a "I know better" stepping stone and call you out on the change - "Where is the test". After 3 years of this behavior what do you think your going to have. A nice clean suite of tests or a monstrous big ball of stubs, mocks, faked, copy pasta that resulted from defensive social coding.
Honestly, write a light suite of tests that get to the point, and keep your ability to code swiftly and make changes quickly. As the code matures you'll find the trouble spots and focus testing on those areas. Don't blindly follow methodologies that are going to have…
I built my last company with Go, Vue.js, gRPC, and Kubernetes. And, wow was it a slog. It was hard for new developers to jump in, we had separate engineers for frontend + backend, and there were things we never even touched - such as real-time/websockets or end-to-end testing.
I recently started a new project in the same stack [1], and partway through paused because it was taking so long to deliver customer value. I jumped back into Rails, inspired by their new Hotwired framework [2]. And, holy shit - the speed of development became insanely quick. Not only that, but I've been able to achieve a higher level of polish than I would have with a single-page JS app. Specifically - building in real-time updates only took a few lines of code. It was possible to achieve this level of polish with an SPA, but realistically it would have taken me so much time that I would have never prioritized it.
I hadn't touched Rails in over a decade. As a junior developer, I felt overwhelmed because Rails had solutions for so many problems I had not yet encountered. But, returning to Rails with some more experience - I appreciate its "omakase" [4] approach so much more. So many best practices are built in or easy-to-install. Queues, cron, async jobs, audit logs, email click tracking, end-to-end testing, real-time updates, caching, sessions, URL slug "friendly IDs", image resizing, rate limiting, admin interfaces, rich text fields, S3 integrations, sitemaps - they all just work!
With the new…
the core of TDD was to iterate by making a failing test, then making it pass
Yes and when I first heard that 15 years ago (or whenever it was) it sounded like genius and I did it on a few projects.
Its a useful crutch maybe if you're doing something very unfamiliar
But as a dogma that must always be followed? And having a test for every single 'specification' and edge case? No - screw that, you end up faffing around for ages setting up mocks and testing stuff that doesnt deserve it. And you end up with a massive library of tests that get on your nerves.
Also everything got completely bent out of shape for a few years because everything had to be injectable and testable.
https://dhh.dk/2014/tdd-is-dead-long-live-testing.html
http://web.archive.org/web/20180215225218/https://iansommerville.com/systems-software-and-technology/giving-up-on-test-first-development/
http://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html
https://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf
It is so long ago now that I can't even find stuff about it on Google -- 2006 I think?
Basically, well after they'd released the framework they had to change the Rails scaffolding (and I think their own apps) because parts of the scaffolding (CRUD deletes) would use GET to do the action that should only be done as POST. They discovered it about the time link-prefetching was invented ;-)
Basically, browsers were pre-fetching all the clever scaffolded delete URLs and destroying records, seemingly at random.
I was really impressed with Rails at the time but stayed well away from the scaffolded code in production, and I was gobsmacked by the idea that people talking about a clever, opinionated framework that did the right thing and produced magical, clean, elegant code to rescue you from terrible alternatives did not understand that GET requests should be idempotent.
Edit: here is one bit about it:
https://dhh.dk/arc/000455.html
The unearned high-mindedness of this still makes me chuckle.
Rails takes this approach[1] to some extent, but it's still possible to plug in different things, it's still all flexible enough to build any type of web app pretty much, and it still requires a ton of developer (or LLM) involvement.
I'm thinking of going far further, to the point that perhaps we should use a new language designed only for web-app development. I'm thinking about removing almost all options so that the LLM only gets to write custom business logic and data modelling and doesn't need to do much else. Again this is all at the cost of being more generally applicable, but I see a lot of software that is fundamentally CRUDL and it's still hard to build well, and I also see a lot of LLMs reinventing the wheel but implementing too many sides on that wheel. They need guardrails.
[1]: https://dhh.dk/2012/rails-is-omakase.html
Link prefetching is generally something you would want a website to retain control over, because it can distort stats, cause resource starvation, and even (when web developers are idiots) cause things like deletions (when a clickable link has a destructive outcome).
I am reminded of the infamous time when DHH had to have it explained to him that GET requests shouldn’t have side effects, after the Rails scaffolding generated CRUD deletes on GET requests.
https://dhh.dk/arc/000454.html
Google were not doing anything wrong here, and DHH was merely trying to deflect blame for the incompetence of the Rails design.
But the fact remains, alas, that this kind of pattern of mistakes is so common, prefetching by default has risks.