> Complexity exists, you can't make it go away, managing it and understanding it is the only thing you can do. Simple systems only displace complexity.
I interpreted that one as a suggestion to avoid welcoming needless complexity because of the false sense of pride it gives you to successfully manage that complexity.
To give an example, I believe C++'s enduring popularity is mostly because of exactly this false sense of pride. You practically need a doctorate-level understanding of the language to use most of its features without stepping on the dozen landmines the language places in your way (I'm so smart because I: remembered to declare my destructors virtual and understand why; can interpret this 2MB of template errors in the compiler output; can """cleverly""" use operator overloads). It can feel nice to be a master of such a complex tool, but that's a false sense of pride. The complexity of your tooling is not the point; the end product is.
There are modern languages trying to eat C++ lunch, like Zig and Rust, but you don't get decades of backward compatibility, and they are not particularly simple either. Rust in particular is one of the most complex programming language in use today, it could definitely be simplified by removing the borrow checker and lifetime things and make "unsafe" implicit, leave memory safety to the programmer. But it makes no sense because Rust was designed for memory safety and performance, which is a complex problem, and therefore Rust is complex.
Yeah, because Bjarne was figuring it out as he went. Which is fair, he was treading new ground. But C++ would have turned out a lot better if he'd taken a lot more vacation time.
The real frustrating part is claiming to embrace errors by returns, but then still just panicing all over the place, dependencies piled upon dependencies piled upon dependencies, just about 0 documentation on doing anything asynchronous without external dependencies, important features being kept in unstable for basically ever, one of the highest barriers to contribute to language features, highly questionable leadership processes and the worst of all:
Openly embracing design complexity. When I learned about extension traits for the first time, I thought "That's awesome", only to find not much later crates, that seem to have some features, which I couldn't find the implementation for anywhere. Turns out external crates were pulled in, which then were used to extend anything carrying certain marker-traits from the previous crate. Like WHY?
Thousands of companies collectively have billions of lines of code in C++. Millions of programmers know it well enough to get the job done. Entire ecosystems with absolutely huge areas are well defined by C++ (and previously C).
Rewriting all this code would be a gargantuan task. It all mostly works (yes, it has bugs, lots of them, but it is still mostly doing the job). The "R" in "ROI" for rewriting it is extremely low and hard to predict, and the "I" is very high.
And that is why old programming languages live on. Not because people take pride in being geniuses or the ability to code in it, but because inertia is really hard to change.
This isn't too many comments, it's a poor quality comment. Try:
time++; // advance 1 simulated second
time++;
That seem obvious enough to me without any comments.When I'm looking at a test case is broken, I ideally want context IN the actual test that lets me understand what the test author was thinking when they wrote it. Why does this test exist as it does? Why are the expectations that are in place valid? Write the comments for you-in-2-years.
If your project doesn't have that convention such that everyone knows than the code should be
timeMs++;
You may also have a time type and so you can use your IDE to examine the type.
networkTimeMs++; // Callback occurs after timeout
timeSec++; // Advance time to check whether dependent properties update
utcTime++; // Leap second, DON'T advance ntpTimeAlso a terrible solution!
The code suffers from primitive obsession. Unless you're in a code section that is known to have performance issues, use real types.
time = time.plusMilliseconds(1);time += Duration::from_millis(1);
But I would expect that "time unit should be in the variable name" is a reasonable choice in a language which doesn't have this affordance, and I needn't care about performance because apparently the language doesn't either.
I also wonder why we've named this variable "time". Maybe we're a very abstract piece of software and so we know nothing more specific? I would prefer to name it e.g. "timeout" or "flight" or "exam_finishes" if we know why we care about this.
I just started yesterday! No, I don't.
int time; // in seconds
/* thousands of lines away or in another file */
time += 1;
Later we change the time to be in milliseconds. We update the comment on the declaration, but now that code is wrong and we have no reason to know that.That's a bad choice, languages should do better (and some do - where they do, use the better features and this problem vanishes) but when it's forced upon us it makes sense to either put the unit in the name of the variable or ensure comments about changes to the variable explain the units consistently, even though that's lots of work. This extra work was dumped on you by the language.
I tend to be a minimalist when writing comments. If I have to write out a comment to describe what I'm doing (like "advance 1 simulated second"), then I have failed at writing clear code. Sometimes I will write a comment to explain why I am doing something, if it's not clear (like "manually advance time to work around frobbing bug in foobar").
Comments add to your maintenance burden. Writing clearer code can often reduce that burden.
1. what units? I was just caught in this with a function with a timeout. I had to look at the docs to find out this was actually in nanoseconds (stuff like this is why I came more around to verbose parameter names).
2. what's the function of the timer?
3. (potential code smell) Do I need to manually increment such a timer for the test? is the time library a necessary part of the test (or perhaps what we testing)?
seconds++;
That gets the idea across very clearly to me and has the benefit of (likely) making the rest of the code clearer tootypes for the win
You can never think enough up front to know all you need to know, or even 95%. You're not omniscient enough, and you never will be. Big Design Up Front fails because of this - you have to be able to iterate.
You also have to know what you're trying to build, and at least roughly how you're going to build it. If you don't, no amount of iteration and experimentation will enable you to converge on a solution. You need to experiment and iterate and explore within at least a sketch of a larger picture, not on a blank canvas.
My thoughts, exactly. And considering that so much unnecessary complexity keeps being added to software (through poor understanding of requirements, technical debt, etc), it's an extremely valuable skill.
- There is no pride in managing or understanding complexity
Complexity exists, you can't make it go away, managing it and understanding it is the only thing you can do. Simple systems only displace complexity.
- Java is a great language because it's boring
That is if you write Java the boring way. A lot of Java code (looking at you Spring) is everything but boring, and it is not fun either.
- Most programming should be done long before a single line of code is written
I went the opposite extreme. That is, if you are not writing code, you are not programming. If you are not writing code on your first day your are wasting time. It is a personal opinion, but the idea is that without doing something concrete, i.e. writing code, it is too easy to lose track of the reality, the reality being that in the end, you will have a program that runs on a machine. It doesn't mean you will have to keep that code.
- Formal modeling and analysis is an essential skill set
Maybe that explains our difference with regard to the last point. Given the opportunity, I prefer try stuff rather than formalize. It is not that formal modeling is useless, it is just less essential to me than experimentation. To quote Don Knuth out of context: "Beware of bugs in the above code; I have only proved it correct, not tried it." ;)
- You literally cannot add too many comments to test code (I challenge anyone to try)
time++; // increment time