The programming languages you use, and the variety of languages you learn, deeply influence how you think about software design.
Software would be much more reliable (in general) if Erlang had become one of the dominant languages for development.
Go sacrifices too much for superficial simplicity; but I would like to see a language that's nearly as easy to learn, but has a better type system and fewer footguns.
Unit testing is often overrated. It is not good for discovering or protecting against most bugs.
Build/test/deploy infrastructure is a genuinely hard problem that needs better tooling, particularly for testability.
hard agree. I'm sick of all the "it doesn't matter what languages you learn" talk because it's just not true. yes, there are broad foundational skills that apply to nearly every language, but beyond that, you're specializing, and the way the language you choose is designed will stick with you. I've seen the concept that you get accents in programming languages just as much as spoken ones, and I think it's totally true - when you learn a new language, you're bringing your habits and assumptions from the last one. so to be honest I don't care about the language design opinions of people who only know dynamically typed languages
Build/test/deploy infrastructure is a genuinely hard problem that needs better tooling, particularly for testability. (Naturally, this is a hard problem, but I think very few developers are working on it.)
Agreed and it's not treated as one which is a compounding issue. 😬
I think unit testing is good at enforcing a spec so other developers know what to expect from your module and how to maintain it. It also kinda force you to dogfood your own stuff. I see a lot of hot takes about unit tests (its okay, this is why we're here), but I am a bit curious how many people here worked in big chaotic software companies before and not just hobby project or small teams.
Imo it is strictly something you do when you write something like a library or any sort of module that you expect other developers to interact with. I've seen teams get all smug about code coverage as if this made them diligent. My personal experience is that developers who understand why unit tests are important tend to write better code than those who skip them or do it "just because".
If I explain myself or add nuance, it won't be a "hot take" anymore, but here goes...
I've mostly worked on small teams, but have maintained fairly large codebases (I think the largest single repo I've worked in was about 1.5 million LoC). I've also maintained software used by other teams, though in fairly small software organizations (under 100 developers total).
I've written code with 100% unit test coverage, and code with almost no coverage. The 100% coverage was in a library that was intended to be core functionality shared across multiple teams.
I definitely agree that they can be useful as both usage examples and as a way to enforce some rules (and consistency) in the API. But I'm not sure I'd go so far as to call that a "spec", because it's too easy to make a breaking change that isn't detected by unit tests. I also feel that mocking is often needed to prevent unit tests from becoming integration tests, but it often hides real errors while excessively limiting the amount of code that's actually being exercised.
I also think that actual integration tests are often a better demonstration of your API than unit tests are.
I haven't used any of these methods as much as I'd like to, but I suspect that each of them is strictly more useful than standard unit testing:
contract tests (arguably just a form of unit testing, but not the way I've generally seen it done)
property-based tests
mutation tests
preconditions & postconditions (with language support)
Makes a lot of sense. I figure contract tests is more or less what I have been doing then.
I think there is that misconception that unit tests are about validating each line of code and preventing logic bugs. Though obviously you understand that this isn't just about that, or not at all about that I would argue. Unit tests won't prevent accidental breaking changes, but you can at least add new tests every time this happen, so you're at least guaranteed that the next maintainer won't be doing the same mistake.
In an ideal world we could probably have nothing but integration tests, but in my experience if you only do integration testing you end up working really hard maintaining tests that are prone to break and costly to run. Unit tests are cheap to code and cheap to run, it is a great place to enforce your "contracts" with tests that are theoretically immutable. After those tests are out of the way, you can focus only on the actual interaction between your systems with the more expensive tests.
Anyway, you have a good take I am just blabbering. This is based on my personal experience as someone who only cared integration tests but was later converted by a person much smarter than I am. And then later on by joining a team that exclusively did integration testing, hundred of tests. It was hell (imo), we had to change dozens of tests for every little bit of modification. The rest of the team seemed fine with it though. We rarely shipped bugs but progress was incredibly slow for a module of such low complexity. Testing wasn't the only issue with that codebase though.
The one part of that that sounds weird to me is needing to change integration tests frequently when changing the code. Were you changing the behavior of existing functionality a lot?
Yes, and the test suites were insane. The program was outputting a lot of data, and we basically asserted on anything and everything for any given integration. I mentioned that testing wasn't the only issue, well there was a lot of issues. Unfortunately the behaviour changes were requested by the stakeholders and there was no way around it. That being said, had this thing we maintained been properly developed those changes would have been a breeze imo. The actual requirements were very simple.
But anyway, I realize this is maybe an extreme example to paint integration tests negatively, but the point remain. In this scenario, every time we changed a bit of code it broke dozens of integration tests instead of breaking just a relevant integration test, had everything that could have been unit tested been written that way. The integration tests could probably also had been less... exhaustive, but it was probably for the best considering the codebase.
Go sacrifices too much for superficial simplicity; but I would like to see a language that's nearly as easy to learn, but has a better type system and fewer footguns.
"Easy to learn" and "good type system" will by necessity be opposing forces IMO. If you want to work with a good type system you're gonna have to put in the effort, I'm not sure there's this magical formulation of a good type system that's also intuitive for most new developers. Hope to be proven wrong one day tho but so far no dice.
I think TypeScript has a pretty good type system, and it's not too hard to learn. Adding sum types (i.e. enums or tagged unions) to Go would be a huge improvement without making it much harder to learn. I also think that requiring nullability to be annotated (like for primitives in C#, but for everything) would be a good feature for a simple type system. (Of course that idea isn't compatible with Go for various reasons.)
I also think that even before "proper" generics were added, Go should have provided the ability to represent and interact with "a slice (or map) of some type" in some way other than just interface{}. This would have needed dedicated syntax, but since slice and map are the only container types and already have special syntax, I don't think it would have been that bad.