3 ms·
Tests are good, I would even go as far to say I love tests. However tests are often used or at least attempted to be used as a crutch for poor language/tooling
by jpgvm 3y ago
Tests are good, I would even go as far to say I love tests.
However tests are often used or at least attempted to be used as a crutch for poor language/tooling/architecture decisions.
The path to code that breaks less is in good decision making (ideally early in the process) to ensure as much as possible the code you write is "obviously correct". Part of this is languages that protect you from silly mistakes, i.e statically compiled, strongly typed, etc and tools/libraries that help you write good code and make use of constructs that are easier to make "obviously correct". i.e sequential/blocking code styles vs async/callback styles. (obviously there is no such thing as "obviously correct" in an absolute sense, hence the air-quotes but some approximation thereof)
Similarly architecture plays a big role. If you concoct a pile of separate micro-services that then all need to be integrated tested together because you didn't settle on an IDL and contract between them etc then you are in for a world of hurt.
Some of it just comes down to principals also. Prefer idempotency. Try design around streaming systems instead of job queues if you can so you get replayability out of the box. Focus on data first, a good schema and using your DB correctly can save you thousands of lines of codes and all the tests that go with them by preventing bad states from ever possibly existing, etc.
If you don't give yourself a good foundation and try to "fix" it with tests and "process" you are doomed to a bad codebase that will just get worse with a test suite that seems to grow forever that either doesn't meaningfully reduce defect rate or if it does then it drastically reduces velocity.
The golden happy path has tests and process but only just enough of each to keep things in check, the good decisions are meant to carry most of the weight.
- nyssos 3y ago> make use of constructs that are easier to make "obviously correct". i.e sequential/blocking code styles vs async/callback styles. I agree with the overall sentiment but I don't think this is a good example. If `await foo(); await bar(); await baz();` is subtly wrong, then `foo(); bar(); baz();` isn't obviously right. Continuation passing style is a minefield, of course, but callback hell is a historical artifact, not an intrinsic feature of asynchronous code.
- jpgvm 3y agoProbably should have been more specific that I was talking about the API styles rather than the runtime. Namely callbacks and CSP which you mentioned but also rx which probably wasn't that popular outside of JVM land but still haunts me. If you `await` every async call then sure, at that point you have made the code sequential within that logical thread of execution, if anything proving my point that it makes it easier to be sure it's correct. I would say that once you aren't doing that, i.e doing real concurrency within a single "thread" of execution then that is going to be less obviously correct then simply not being concurrent. That said, broadly speaking async/await is sequential/blocking enough for my taste. There are runtime/language specific problems with it that reduce it's obvious correctness IMO (unhandled promise rejection when not immediately awaiting on Node for example) but it's not an API problem. TLDR: async/await good.