Absolute truths I unlearned as junior developer
monicalent.com
monicalent.com
Learned as junior: If you report an OS bug or some other deep problem, seniors will not believe you and assume you're making excuses for your own bugs and lack of understanding.
Understood as senior: If a junior programmer tells me they found a system-level bug, I won't believe them and will tell them to go figure out what's wrong with their code.
Learned as junior: Legacy code is hard to read.
Understood as senior: Legacy code that I wrote myself is hard to read.
Learned as junior: Technical skills matter most.
Understood as senior: Communication skills matter most.
Learned as junior: New tech solves old problems.
Understood as senior: New tech creates new problems.
can you give examples ?
Sometimes this worked. Sometimes it really, really didn't. It also meant we had a very difficult time discussing larger architectural questions with him or giving him useful feedback on his code.
Junior dev me: meetings are a waste of time.
Senior dev me: meetings are the steering wheel, the developers are the engine (https://kitsunesoftware.wordpress.com/2018/01/29/utility-fun...)
Teaching juniors can be more productive than coding. Being 10x by yourself is less good than 3x-ing a whole team. Even better, teach everyone to be as fast & good as you. Good teaching requires good communication and building trust.
Understanding priorities and goals is absolutely critical to making good choices while programming. Writing good code under reasonable deadlines in an organization necessarily involves a lot of discussion about what constitutes an acceptable solution, what doesn’t, how long it might take, how long is too long, what features are nice but not necessary.
Over-engineering, for example, is extremely common, and is caused in part by not correctly balancing goals and priorities with time budgets. It’s usually a symptom of mis-communication.
Can’t even count how many times I’ve seen a programmer go off the rails building stuff that wasn’t asked for, only to have a meeting several weeks later that invalidated weeks of work when the goals were clarified. (That includes me, btw.)
Making large changes and leading a group of programmers often requires a lot of convincing and rallying work along with the technical planning, sometimes much more than you’d expect. It also requires the ability to put yourself aside and allow others to contribute to the design, even when you think your technical solution is superior.
Getting promoted is, in my experience, most commonly a process of demonstrating to others that you listen well, organize well, work well with others, get things done under deadlines, understand and report what juniors are doing to management, budget well, internalize the organizational goals and contribute meaningfully to meeting those goals.
In short, it’s because teamwork is important.
Moreover junior usually have hard time expressing techincal problem.
Being able to couple value to what you do, or for that matter, to what might not be worth pursuing, is a very good skill. And be sure to not often say no to a PM asking for a feature, but rather give an alternative "doing that is quite complex and might take us half a year. How about we do this other thing that gets us 90% of the way there, but only take a week to implement?"
> Legacy code that I wrote myself is hard to read.
Sometimes I don’t even recognize me as the author for a while. Realizing I’m reading something I wrote and can’t understand it without studying carefully has been rather surprising and reminds me of the old Kernighan quote “Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?”
My goals used to be to write code that looked and felt cool to myself and others, to add features in clever ways with as little change to the function signatures and structure as possible so as to not disturb the architecture. While keeping changes small is a good goal to balance, it’s always possible to be too small and add unnamed concepts and fail to restructure around new concepts when you should. Do that a few times in a row (which I have done) and you end up with architectural spaghetti code that might look clean and modular at first glance but becomes a maintenance nightmare. My goals are now to: - make code so easy to read it’s boring, because if it looks interesting, it’s probably too clever - and to identify and name all the unnamed concepts and refactor often to accommodate new features.
This happened to me just yesterday! I was helping a co-worker with a problem, and I noticed some redundant code in the same function, so I told him he could simplify it while he was there. His response was, "...but you wrote that". (And as it turns out, only a few days prior!)
In fact, I often conciously refrain from using blame on an "interesting" piece of code because it doesn't matter who wrote it. Looking it up would just satisfy idle curiosity, but yield noninsight into how to improve the code. In fact, I think that blame should just list the commits, but hide the authors by default. "What changed? " and "how?" are always much more pressing questions than "who?".
Pinning bad work on a person does not make progress. Fixing bad code does.
It's an endemic issue, core to the problem of poor software.
> My goals used to be to write code that looked and felt cool to myself and others, ... My goals are now to: - make code so easy to read it’s boring
Same for me, and I'm sure same to many of the folks that have advanced past senior level. The problem of other people, other senior people, needing to read, understand, and significantly modify your code is one of the reasons why you can't really advance past senior in a startup. There aren't enough experienced folks that you have to write "up to". Of course the other part of being post-senior is the ability to scale your expertise, but now I'm digressing quite far. Then once you're that advanced, you don't want to take a pay and scope cut to work at a startup. This is a major contributor to startup technical debt accumulation, one that can't readily be solved.
I still do write "cool code", for code that I will only use myself that doesn't go into production. But for all others, I write easy-to-read code. And I review code with that in mind. When comments have a typo that alter the meaning of the comment, I insist that be fixed. Juniors hate me for being too picky. (and I hate them for being too sloppy.)
Remember, the most important part of your job as senior+ is not what you do yourself, it's how you guide others.
What classes as 'senior' that their own coding style has changed that much in 6 months? O.o
> I still do write "cool code", for code that I will only use myself that doesn't go into production. But for all others, I write easy-to-read code.
Good dev. Remember, you are not your audience. Unless you're just writing play-code in which case go nuts and be as 'clever' as you want. :D
> When comments have a typo that alter the meaning of the comment, I insist that be fixed. Juniors hate me for being too picky.
Then they're wrong, and tbh that's not something I'd accept more than once from an employee.
> What classes as 'senior' that their own coding style has changed that much in 6 months? O.o
Your code style can remain exactly the same, and it would still happen. The reason is not that you would write the code differently today, but that you forgot the issues and edge cases that made you write it like that then, and that at the time you focused too much on the writing, not on the reading.
I mean really, "this code is unfamiliar therefore it's messy therefore we should rewrite it" is a flaming red flag. Chesterton's fence, people!
This is routine for ordinary cultural practices as well. However, the common explanation for a given cultural practice usually has nothing to do with the actual reasons it might be a good idea.
But the wrong lesson seems to be taken away from this. It’s not that all code is shit - it’s that you aren’t good at reading the code yet if you can’t see all the little hairs and bug fixes.
several projects I've come in to - yeah, the code what shit, and yeah, I could do better. And I've done better. By asking questions, documenting the answers, writing sample data, and writing tests.
I get that code can be sloppy, have edge cases, etc. Took over a project that was halfway migrated from CI to Laravel. The migrator had close to a year on this 'migration'. We had not one unit test case, no migrations, no seeders, no integration tests when we took over. What we had was piles of half-baked uncommented model code, over-reliance on magic methods, Laravel/CI models with the same names and method names often being used in the same request but with unintentionally dissimilar behavior.
The 'code' isn't the (whole) problem. All the other stuff around the code that provides the context is the problem. We had ~ 20 tickets in a tracking system with vague notes, and were given 5 email threads of discussion about functionality questions, none with actual resolution.
> it’s that you aren’t good at reading the code yet if you can’t see all the little hairs and bug fixes.
Or... the person writing it before you simply didn't know how to write/document.
Sometimes - really, honestly - you can actually "do it better" because... really, honestly, sometimes you are actually better - more competent, more experienced, more diligent, more professional - than the person who left the code you're working on. Not always, but not never.
In my experience the latter case is far more common. But I suppose experiences will differ dramatically depending on what you work on.
I've advised a number of folks to care less about the code style, and focus more on making it at least understandable. I don't particularly care if you're using a factory pattern or not, but please do doc/comment someplace what the expected behavior for your 'backordering' logic is. I can fix things later if I understand what was intended, vs just what I have to guess at later.
Have worked on some projects in the last few years that are 'bad' from code perspective. One is bad, but the company as a whole operates... decently, and is improving, and more importantly, is providing a lot of value to their customers. The customers tolerate some bugs now and then because a) they still get value and b) the issues are addressed. There's a full process for changes/fixes/rollouts, and the team overall understands that there's tech debt to deal with. Some folks understand that they're still paying off tech debt from 3-4 years ago, and understand those decisions were bad, and try to avoid those same mistakes.
Hundreds of integration and unit tests (growing every week) help grow the confidence levels, and remove barriers to smaller refactoring efforts, because there's a reasonable way to detect the impact of changes. It's not perfect, but that's also understood and accepted.
Another one is the CI/Laravel situation from above. Small company, no real 'tech' people on staff - it's all 'outsourced'. They're frustrated because they see other companies progressing faster than they do, and everything seems to take 5x longer than they expect. It's because the code is bad (on many levels). If we were not trying to make any changes, and it just ran in its current state, it would still continue to make money for them, but they want new features, which requires actually understanding how everything fits together. It took two people several months to have a reasonable understanding of how all the running parts fit together (while also trying to add new features/etc), and finally get a small number of unit tests in place.
My coding style hasn't really changed in years - frankly, I don't write a lot of code, I do other things. But I often run into situations where I'm irritated at my own bad code from months earlier, when the shortcomings of the code are actually driven by things I know now that I didn't know then.
Of course you can stall any junior merge request until it looks like senior code, but at that point you might as well write it yourself.
If bad code is getting merged, that's tech debt / time someone else is going to have to spend anyway, plus the time needed to identify the issue and triage down the line. I would think it's better investment to use that time up front and help the jumior level up too.
This quote can be interpreted in an intelligence-positive way, to encourage you to learn by writing the cleverest possible code. Then, when you get to debug it, you will be forced to improve your skills. This interpretation is called Kernighan's lever [1] and it is very beautiful. The alternative is a life of boredom where you don't learn anything new.
[1] https://www.linusakesson.net/programming/kernighans-lever/in...
When I try something new, usually what I learn is not that my assumptions were correct, but the more interesting part is where my assumptions were wrong. This usually only becomes apparent either when writing the code, or in some of the more interesting cases, when debugging.
Now that's an impressive logical leap.
”Isn’t this a wonderful learning opportunity for all of us”
Debugging time is never a good time to start honing new skills.
It sounds like spaghetti code but even worse because it’s covered with a messy layer of beans and guacamole and wrapped in layers of tortilla.
Next up: some doofus who learned high-order Haskell in middle school, and takes offense of me calling it complicated.
I would claim that any clever final result can be accomplished without any need for intermediate cleverness. Things that look clever to other people but are intuitive to you might be OK. Things that look clever to you should be avoided when possible.
Simple guidelines to live your life by ;)
(I do not know who first replied, ‘I do know where I live.’)
¹ https://groups.google.com/forum/#!msg/comp.lang.c++/rYCO5yn4...
Ha, yes, I occasionally come across code I wrote years before and have a few "WTF moments"!
For me, any code that I wrote more than 3 weeks, I forgot. That's why I comment the hell out of my code. The younger programmers have routinely told me "commented code means the code isn't very good." I chuckle and ignore them and wait for them to hit their mid-30s and older.
Descriptive types, clear tests, and sensible variable names are much more effective strategies for making code understandable. Comments should be a last-resort stopgap.
I'd add; Comments should be saying _why_ this crazy method is here. You can always parse the code to figure out what it does. In a few months/years (depending on your memory) you will not remember _why_ this code was put in place.
Comments don't have to decay. Discipline is important. Culture is important. And yes, these have to be intentionally set and upheld.
If you set a culture of discipline around maintaining the comments with the code, and ensuring they are updated, then it's really not that hard to do it. If the developer doesn't remember to do it when making changes, then the code reviewer can catch it and enforce it.
And nothing really substitutes for an english language explanation of the "why" and the intention of a particular section of code. A good comment explaining why something was done a particular way, or what the code was intended to accomplish, can save hours of walking up and down call stacks. It's also something that cannot be communicated through unit tests, or even integration tests, a lot of the time. Those communicate the "what" and the "how" - not the "why".
To be equally brutally honest: right back at you. I would trust the quality of those I've worked with over those who believe in comments, any day of the week.
My point was simply that I started as a believer in comments when I was more junior, and became anti-comment through experience. So even if we believe senior people are more likely to be right than junior people (which I very much doubt, frankly), that tells us little about whether comments are good or not.
> If you set a culture of discipline around maintaining the comments with the code, and ensuring they are updated, then it's really not that hard to do it.
Human programmers have a limited discipline budget, and if you're spending it on keeping the comments up to date then you're not spending it on other things. Yes, you can use manual effort to keep code explanations up to date, just as you can use manual effort to ensure that you don't use memory after it's freed, or that your code is formatted consistently, or that the tests were run before a PR is merged. But you're better off automating those things and saving your manual effort for the things that can't be automated.
> And nothing really substitutes for an english language explanation of the "why" and the intention of a particular section of code.
Disagree; code can be much more precise and clear than English, that's its great advantage. As the saying goes, the code is for humans to understand, and only incidentally for the computer to execute. The whole point of coding declaratively is that the "why" is front and center and the "what"/"how" follows from that.
As a common feature of most higher-level languages is that they co-opt natural language terms (and also mathematical notation, which is an option in commenting) with the intent to increase clarity, can you show us an example where code is more clear than natural language in explaining both what it is doing and why?
If you are working in something like APL, I can see there might be a case...
I am not so much interested in the precision issue, as both code and language can be very precisely wrong or right.
Agreed, code is much more precise than English. But precision is not the same thing as being meaningful and without context, precision is useless. Code generally sucks at context, which is why every programming language worth its salt has comments.
Do you never see code that has global side effects? Or that is written a particular way to take advantage of the hardware that it is running on? Or any other of the many ways that the intention and meaning of a piece of code within the codebase it exists in can be not immediately obvious?
The answer for modern languages and frameworks is "write pure functions."
>Or that is written a particular way to take advantage of the hardware that it is running on?
Move to service/helper/utility class for that particular hardware or with a name that clarifies it's for that particular hardware.
I find comments to be necessary very rarely. Atm looking at a codebase where they are made to cover up for a lack of desire to think.
This is not always possible, and in those cases I also strongly prefer well written, concise comments explaining what is going on and why, ideally with a link to a reference/source which explains the background.
Some examples of method names:
- generateTreeToAllowPartitioningOfItems(...)
- getMatchingRegularizationPenaltyForSpecialCaseX(...)
- getShortTermRedisProxyCache(...)
- createNewPrefilledTemplateObjectForXYZ(...)
I hope this doesn't sound snarky. But more often than not comments do date in my experience (and they don't handle refactoring well), while (compiler-known) names are handled as 1st class citizens by the current IDEs and thus are corrected and updated anywhere.
In code reviews we usually aim for "low comment" density, the implementer shouldn't explain what or why he was doing, the reviewer has to understand just from the code (as it would happen if she/he has to maintain it later on). The review is "not good" or even fails if the reviewer doesn't understand why and what is happening. The outcome will in most cases be an improved design, not more comments.
(assuming, which you should always assume imho, that you left the company many years ago when this event occurs)
I've been writing Lisp off and on since late last century, so I know full well the value of declarative code. Preaching to the choir, there! But I can also report that every real program I've ever written (i.e., that had at least one user) needed significant non-declarative parts.
And for those non-declarative parts, you need the "why". Why is this call before that one? Why is this system call used? Why is this constant being passed to the call? And so on. (It's because when you run it on OS ${a} version ${b}, there's a bug in the ${c} library that requires us to force the initialization of the ${d} subsystem before it can ... true story.)
The declarative parts of your program don't require "why" comments, and that's great, but a corollary to that is the parts that can be written in a declarative style aren't the ones that require a "why". Building a DOM structure manually takes a lot of lines of code, but it's all still quite simple, and requires no explanation. Writing a trampoline necessitates a bunch of "why"s, and there's no way to just substitute a declaration for it (without pushing the whole mess somewhere else).
Code is first for humans to understand, and that requires comments, because humans speak English (or some other natural language), and no programming language is yet powerful enough to efficiently (in time or space) express everything that English can.
I've got a trampoline in my codebase to avoid a stack overflow. The why is the test that a certain repeated operation doesn't stack overflow.
There are a number of places where it could've been implemented with one technique or another, but there's no particular reason that the approach I've taken should be better or worse than one of the other options. If there was, I'd want to formalise that (e.g. if I'd chosen one approach because it performed better than another, I'd want a benchmark test that actually checked that).
You think humans are bad, try working with Lobster programmers, they get work done, but their coding style is just horrible (they use tabs).
Declarative means that we specify the "what", and the machine deduces the "how".
There is no room for "why", because our present-day machines do not require motivating argumentation in order to do our bidding. They either need the "what" or the "how", or whatever blend of the two that we find convenient.
We need the "why" in the documentation. Such as: why is this program written in the first place? The "why" is not in the code. When code is clear, it's just easy to understand its "what" or "how", never the "why". Unclear code obscures the "how" or "what", but clear code doesn't reveal "why".
Every "how" breaks down into smaller steps. Of course, those smaller steps have a "why" related to their role in relation to the other steps; that's not the "why" that I'm talking about here. Of course we know why we increment a loop counter when walking though an array: to get to the next element. If you start commenting that kind of why, you will soon be flogged by your team mates.
This doesn't address the parent's criticism. Clear, precise code only tells you what the computer is doing. What it can never tell you is why the computer needs to do it exactly like that.
Software breaks in weird ways when pushed to the limits. The fixes for these edge cases are not always obvious and may not be something that can be replicated with testing.
Without comments, some cowboy can come along and think, "it's flushing a buffer here? that's dumb. <delete>" The change gets put in, passes testing, spends four months in production, when a bug report comes in from a customer complaining about an issue that they had three years ago.
Now someone has to spend a bunch of time figuring out the problem, QAing the fix, then getting it back into production. It's thousands of dollars that the company could have saved if only there was a comment about why that buffer flush was there.
You might think this is some crazy edge case, but it's not.
Reconstructing the original idea or meaning can often involve far more context than local variable and functioning naming can provide.
These are things that are often completely outside your control.
> If you set a culture...
At most shops, you don't get to set the culture. About the only time you do is if you're a founder or early developer. Otherwise you have to fit into the existing culture, or attempt to find a company that better reflects what you want. Sure, it's not hopeless; you can likely influence to some extent, but your influence is usually limited.
> And nothing really substitutes for an english language explanation of the "why" and the intention of a particular section of code.
I do agree with this. Any code that can't be written in a self-documenting way absolutely must be commented. However, if you find the need to do this often, it might be a sign that you should focus more on code clarity and less on (likely premature) optimization, or perhaps consider if you're really using the right tool (language, framework, etc.) for the job at hand.
I will admit that I probably comment less than I should, but I feel like the average is way too verbose, and that enough comments are out of date and incorrect (often in very subtle ways) that it adds significantly to my overhead when trying to understand someone else's (or even my own) code.
They can. Just after correcting all the buffer overflows and before fixing all the use-after-frees. Then the comments can be consumed by all the other teams with the discipline and culture to avoid writing bugs for all time.
Comments don't get updated when the requirements for the code change, more often than not end up as misleading.
The only thing worth commenting are actual libraries that are maintained, and 'magic values'.
I simply like comments for adding things like... so and so told me to do this... or simply documenting weird behavior or weird business logic.
My personal favorite comment style is to wrap a chunk of code in `#{` `#}` blocks and add a general comment of what that chunk of code is accomplishing. Sort of like an inline method.
It does not follow from the possibility for error that a "significant" proportion of comments will necessarily be false. In my experience, that is most likely when an organization has commenting as a mandatory part of its process, which inevitably leads to most comments being trite, and some wrong. Outside of that, comments have not been a problem mainly because they are almost non-existent, even when the code could benefit from them.
Comments are for things like
1) explaining why this thing that looks wrong or dumb, really isn't. 2) explaining what method/function/class/whatever is suppose to do. Because code can be correct, understandable, and still wrong.
That way, I can understand what is actually being done. And I can then re-analyze why I did the shortcuts to get to optimization.
But 99% of the time, we dont need to optimize. CPU/RAM is cheap. But those 1% of the times when you're going from N^2 to N^logN ... Welll.....
Thats what I get for trying to type it on a phone browser!
While I don't like comments that try to explain what code is doing (write better code), comments are very useful for annotating WHY code does what it does. They're also very useful for adding documentation references, code use gotchas and things that need to be addressed in the future.
I hear comment love very frequently from enterprise engineers working with 10+ year old Java codebases, but very infrequently from hackers working with young code bases in more concise languages (complex algorithms aside).
Where I work, I can expect my scalacode to be read by people who can barely write a line of it, and I routinely read typescript and go code while being totally inept at those.
I bless comments that are here to help the reader read, and I let those behind too where there's some specialists-only syntax.
``` doesAThing() //does a thing ```
doesn't help anyone.
My rule: Code is for how, comment is for why.
Excellent. For interfaces, other code that uses the interface (perhaps even tests) can also help to document the "why".
> Understood as senior: Communication skills matter most.
The reason people dismiss comments is usually that they or others around them aren't good at writing useful comments.
Especially when there are linter rules requiring comments you'll have something like def open_thing(x, y): and a comment, "defines a function that opens thing."
Yes, those are pointless. Often what's going on is a person is dumping their stream of consciousness into the comment field.
It takes practice to understand what a reader needs to know. You have to actually practice reading comments and thinking things through (another reason code review is important in your team) to get good at undertanding what you should write down.
All that said, if you truly hate commenting, at least build a habit of descriptive naming and exploiting your type system as fully as possible.
As opposed to telling the boss what you're going to do.
I actually think those comments are useful in two ways:
1. The process of writing a comment will help often help me rename the function/variables so e.g. “defines a function that opens thing” becomes something like “opens can_of_worms with the given instrument and restraints” for the method definition open_can_of_worms(instrument, restraints)
2. You can use variable/return value comments to further restrict the domain of values, e.g. non-null, positive or in the range 1-42 (arguably it would be better to express some of these in the type system, but that is a different discussion). These comments show up in my IDE when I try to call the code in a remote location, so I don’t have to guess or remember the constraints.
(edit formatting)
I couldn't agree more.
A while back I got in the habit of trying to write code for "me, six-months from now". So, if I think I can explain it to "future me", then I'm happy. Ever since I started doing that, I've been much happier with "past me"'s code.
In addition to comments (particularly around hard to grok code), I've also started trying to be as consistent as possible in code structure and naming schemes. This also helps a lot.
Meta comment: This is bullshit and has problems with this that and the other thing. But to fix that I'd have to refactor this other module and I'm not going to do that now. And the other thing I'm drawing a blank.
Meta comment2: I don't think the code needs to do this here. But I can't prove it right now.
Meta comment3: We absolutely need to do this exactly as it is. Because otherwise bad thing happens, which you probably won't see until it hits production.
Meta comment4: This function name isn't correct. But I can't think of a name that is better.
When dealing with articulate code I often rename the same thing multiple time while I understand it better/clarify it's purpose. Also I love how naming protects the purpose of a variable or method, mentally speaking
I used to worry about putting emotional blurbs in comments or commit messages, but I'm starting to see their value. A commit that starts "This ugly writing is to appease Roger, the editor obsessed with AP style" lets me know three things:
- Who asked for the change - The source of the content - The fact I disagree but still do it, so future me doesn't pick fights present me avoided
Of course, it could also mean "TODO: revert this commit the minute Roger retires."
On the other hand, on the rare occasion that I've commented or committed something based on emotions, I've always regretted it. Granted they never caused problems for me, just a source of internal embarrassment. Still a good enough reason to be thoughtful about what emotions you express.
This is the highest purpose that a comment can fulfill - telling why you are doing something that looks stupid.
Sometimes you have a choice of a clever way to do something, which saves a few lines and uses neat language tricks that you rarely use * , or just doing things the boring way. As long as the boring way is obvious enough, it's often the better choice.
* I'm looking at you, Ruby... :)
Most such commits are never looked at again. But every other month or so, I come across a maintenance issue where I wonder about the context of something. In many such cases, I've saved multiple days of false starts or debugging. So it pays off in the long run even if it's only me gaining something from this. (Unlikely; we're a company of 80 developers).
Or you could sneak one more little if statement or some copy/paste in there to fix it instead, and add a little comment that says "If you modify this line, please verify that your change doesn't impact Line XXX of file FFFF as well." And then you're done in less than a day and have saved a huge amount of testing.
Definitely useful in the case where it's near-to-impossible to DRY up something, though. Sadly, the limitations of an industrial C environment have led my code to contain a lot of annoying 'If you add something here, make sure to add it to X struct and Y function' comments.
I've seen code from an otherwise highly capable developer that contained 1000+ LOC functions. When asked why he couldn't do a refactor the answer boiled down to fear. When the only real way to test the code is by physically running a machine through a number of scenarios, many of which are difficult at best to recreate, you become very reluctant to refactor or clean it up.
Like all problems, it's best to nip it in the bud before things get that far out of line.
> Understood as senior: New tech creates new problems.
This is one all the people who push "new and shiny" need to learn.
I don't believe this is the spirit in which this was meant. If the old system is out of date and there are buggy libraries that aren't being maintained, that is a WHOLE different issue.
Even nowadays many languages don't have features that VB6 offered incl. WYSIWYG. Debugging capabilities of modern languages/environments are still often not even close to what VB6 offered 20+ years ago.
C# certainly is outstanding but I think Microsoft made a gigantic mistake by killing VB6 the way they did.
Microsoft's prevented a large amount of people to write applications, since a new ecosystem like C# or VB.net was significantly more difficult to learn and understand.
In retrospect Python or Node probably took VB6's place, so Microsoft just lost out on a huge market there. Bad management decision.
> Learned as junior: Legacy code is hard to read. Understood as senior: Legacy code that I wrote myself is hard to read.
Lemma:
> Learned as junior: Technical skills matter most. Understood as senior: Communication skills matter most.
Theorem:
Communication needs to target the people of the future.
Similarly, one thing I learned is that if I find a bug with an OS or platform, 9 times out of 10 it's actually due to some problem in my code or my own lack of understanding :)
This has happened more times than it probably should:
1. Arrive upon some code I wrote at some point in the near or distant past.
2. Review it to get some idea of what I was trying to do
3. Laugh at my young self for being so naive
4. Refactor or Rewrite
5. Re-realize the edge-cases and difficulties
6. Remember this being a problem
7. Refactor And Rewrite
8. Either `git reset --hard HEAD` or end up with a similar solution, but with better comments
Once in a while, I end up with a [simpler | faster | clearer | otherwise better] version, which makes this process worth while - even with the false positives.
1. Stumble upon some specific problem with a web framework we use.
2. Jump straight to stackoverflow.
3. Sbd had a similar issue, nice.
4. Sbd wrote a very concise answer, nice too.
5. There's my nickname under the answer. Oh, wait...
Yeah I found this bug and I worked through it, did a bunch of googling or internal searching found a similar issue and welp it was a ticket from you (me) 2 years ago.
Monthly I pull out useful things into notes about that topic. Run into a weird problem with spring framework? Copy out the relevant info into springframework.md.
I try really hard not to do that for internal or public forums. The odds are better than you think that you'll stumble on the same topic a few years from now.
On internal tools I do do that but that's a smaller number.
Worse is stuff like car and fridge repair. I have no idea what forum I find qqs on.
I really thought this only happened to me :-)
"Sby" would've made way more sense. Or heaven forbid we use one more character to make the nearly obvious "sbdy."
Why create my own silo of documentation when I can just put the answer where I'm likely going to find it (Stack Overflow).
In a wider sense, you also want to explain why something is NOT done. Ie why certain other ideas don't work, or why we don't offer certain features.
B: Take a look at this shit code that I found.
A: Whoah, it really is shit. Blame it so that we can see what kind of genious is behind this.
B: ...
A: Well?
B: Apparently you wrote and I reviewed/approved it.
I was working on an extraordinarily bad codebase, and stumbled upon some modular, reusable code that made my life way easier. I wondered who wrote this rare gem in that pile of shit, and checked `svn praise` for a change.
It was me.
It would have been the highlight of my then short career, if not for the fact that it meant there were no other semi-competent people on that project.
Basically, not everyone works for Google.
Not that that needs to be said, no matter the company (if it's of any decent size.)
Use "git annotate" instead.
Git blame quickly gives you the commits you are likely interested in, then you can use them as a starting point for your git log digging.
Communication, especially with your future self, is an important skill.
story of my life
I've definitely run into this phenomenon of independently landing on the same design twice because of the same edge cases. At some point back in 2005 or so, I was working on a collision detection component for a physics engine. This was 1-2 years before the Box2D engine, so there was a significant lack of any open source options, so I was rolling my own stuff that was quite similar to Box2D (but Box2D was written much better).
One year later, I came back, looked at the code, thought "this is unreadable!" refactored it, and sure enough, stuff was falling through floors. I went back to my old code, found several comments discussing edge cases having to do with discrete time step problems, and concluded that my old code was in fact the right approach, it just didn't put comments about edge cases high enough in the call stack.
When you know that certain information should not be used in a correct solution, a more abstract approach can make sure that information stays hidden.
A really simple example: for-loops in Python 'leak' their index variable. Stick that loop inside a local function, and then you know that you can not accidentally make use of the index variable later.
A more complicated example is deliberately coding to an interface that carefully exposes only what should be exposed. Eg using a map or filter higher-order function.
1. Found some extremely cool code, marveled how amazing it works
2. Realized I wrote it as a teenager
3. Got depressed, questioning my life decisions
// rather than use the API we parse a scrape here with a
// regex because we signed up and it doesn't have half the fields we want.
like, totally obvious. except six years later when the regex stops matching, and you are already using the API all over the code anyway and you get to this part with the scrape and you don't get it. Are we trying to elude the API requests number limit? or what is the reason for this bizarre scrape.a lot of people would refactor by seeing if they can put the API call in there, but this wastes massive time you could avoid if you knew the reason for this in the first place. and maybe the API still doesn't have it, and so you put the API call in, you try to remember the reason for the scrape, and then you realize that all this is still the best way to do this, you just have to update the regex to continue to match. work that could have been saved by a simple comment telling you the reason it looks this way.
As in "42", the first bit is more difficult.
I think comments can help avoid such scenarios.
My mum told me "if you think you found a bug in the compiler or OS... you're wrong". This advice applies until you're good enough to know it doesn't. She was right.
As it should be. If your code from a five to ten years ago doesn't make you cringe at least a little bit, the right way to view that is not that you were doing a good job back then, but that you haven't gotten any better since then.
Taking that idea to the extreme of not having any feeling of ownership or pride (or lack thereof) in your past work seems rather silly to me. It wasn't just some other programmer, it was you.
I'm not saying you should cringe because the code is bad, but because you should have a sense of "well, I could have saved myself some trouble or made this cleaner/more obvious if I only knew then what I know now."
Me: 'Even a blind squirrel finds a nut once in a while'
Oh you found a 'bug' in Mac OS, Windows, or Linux? Probably not.
Oh you found a 'bug' some open source library? Maybe.
Or in our in house developed framework? Probably.
In house framework I wrote? Certain of that.
Your comment came at the perfect time. I just finished debugging a "high urgency" problem with a program I developed and maintained for the past 10 years.
The program started simple with a small list of rules to apply against data sets. Over time the list became a tree of rules that expanded in both breadth and depth. It was refactored once to get the design in line with the rules of the time.
The "high urgency" issue turned out to be the program working correctly. But, the functional user wasn't able to keep in mind some of the rules he set. It took me a half hour, with lots of "why did I do that", to explain it again to the user.
I can't speak enough on this one. In our craft, The better one is at communication skills, the more effective their technical skills will be.
My first job was in finance and I remember one time that I had a glitch on a complicated Excel spreadsheet. I checked it over, checked again and checked again until I finally concluded that the bug was in Excel itself. So I go to my boss and tell him that the data isn't ready because there's a bug in Excel. I was laughed at by the entire team. They agreed to give me $1000 right then and there if it was really a bug, but if not, I had to admit the shame. Well... of course it wasn't a bug in Excel, I just made a careless, albeit hard-to-find, mistake.
Lesson learned. If millions of people use something, that doesn't mean it doesn't have bugs. But it does mean that you probably aren't going to find those bugs unless you are doing something strange.
oddly, I did once find an actual bug with indexes in Postgres. Because of my earlier experience, I spent a lot of time assuming it was me before I finally isolated it as being a bug with postgres itself. I submitted a bug report and it was patched within a day. But still, 99% of the time, it's me.
If some second year programmer told me that nonsense, I'd make them go back and either find their bug, or write a test program that exercised the bug in isolation (which was what I did).
I was writing a Windows NT (what's that?) device driver for a communications board we developed for one of our products. I kept running into a problem and struggled with it for days. I was experienced enough to understand that the error was probably mine and the problem was so basic that if it was in the chip pretty much everyone who tried to use it in that mode would be screaming about it not working. The chip was an 8-channel UART (serial converter) and IIRC, the bug was in one of the FIFO interrupt modes.
Finally I gave up, got the phone number for the chip vendor's (I think it was Texas Instruments) local Field Applications Engineer and explained the problem to him. "Oh, yeah, we know about that bug, there's a new version of the chip about to be released. You guys are actually only the second customer to sample that chip. Lucky we found the problem before it went into production!"
One noob came to me with a serious codegen bug in GCC, where even with `-O0` it would fail to correctly run a trivial for loop. Another found a huge security hole in `sudo` that gave everyone unrestricted access. My favorite was one who asked if the JDK standard library had any known bugs processing the letter "g".
They all turned out to be user error, if you can believe it.
Even though I'm still juniorish, I still run into issues like this that stump me. But then occasionally you do find bugs with existing software which keeps you second guessing everything. Usually those bugs come from using two things in conjunction that haven't been well tested together.
Also sometimes, you find a bug that isn't accepted by the software vendor/owning team as a bug, because it has some sort of obscure work around that would take you a week of tinkering to figure out. Those "aren't bugs" but yeah, they are bugs. Software vendors that also sell consulting and related services love to pull shit like that
But I also have a good counter-example story:
Some day I found an HTTP rfc violation in one of very popular oss HTTP client libraries. I filed a bug report with detailed reproduction. It was closed immediately, with "works as designed, you misunderstood HTTP". Then we had a debate in ticket comments for many days and I couldn't convince them to the right interpretation of HTTP (I admit, the text is not easy sometimes). Finally I posted a message on HTTP mailing list and Roy Fielding confirmed I was right. They reopened and fixed. I must say this is really hard thing to argue with somebody with an edge in experience and not come out as arrogant.
In particular - when somebody responds with "I have more experience / I've been doing it for 20 years, and you say I'm wrong?". How to best handle such cases?
I wish I knew. I try to limit the discussion to the purely technical, or to barely acknowledge it as in "sure, but RFC123 says X and Y implements it that way as shown in Z".
Of course, that goes for when I'm the authority as well. I don't care who is correct, I care about what.
Related: There are no new problems.
>Understood as senior: Communication skills matter most.
I see this often, but it needs to be said with a caveat - the second line presupposes the first. Without the first, the second doesn't really matter (or it does but you're in the wrong career).
I've been of a similar opinion at some point, but now that I see people _optimizing for communication_ as juniors in lieu of actual technical skills, I say: both matter a ton. I hate dealing with a junior that is an extremely good people person but a terrible developer: they tend to think they got everything covered just because people like them so much, even when their actual solutions are terrible.
> Understood as senior: New tech creates new problems.
Yep! When you use "new tech", you're making a bet, and not all bets pay off. If you're cautious, you'll hold off until new tech is proven (or pilot it cautiously before migrating). If you're possessed with good judgment, you'll be discriminating in which new techs you adopt. If you have both virtues, you may even gain a competitive advantage.
And, yet, it does happen. The important part as the senior guy is to make the junior guy create a test case and then cut it to the bone until it is obvious where the bug is.
Story time: It's mid ninteen-ninety-mumble and your intrepid hero is a junior programmer handling multi-site integration and testing tool infrastructure. This being the time when the Swiss Army Chainsaw(tm) (aka Perl 4) is well and truly entrenched in the sysadmin and toolsmith programmers, my technical superiors throw me a couple of Perl books, set my deliverable date impossibly soon, and tell me to get going post haste.
So, I code. It's not a lot of code, but it is parsing and matching files in 3 different formats from 3 different sites. Of course, I hear what you are saying: "Perl and parsing is like mixing ammonia and chlorine--and probably more painful." Yes, I concur. But, it is the tool at hand in the long forgotten mists of time when RAM was expensive and spinning rust still resembled an iron brick. So, off I go with regexes for parsing (Yes, I know, now I have 3 problems).
And everything worked quite swimmingly. Except for a bit of idiocy that nobody could track down that occasionally flagged a couple of records as mismatched when manual inspection showed they really were not. Nobody minded that much as the scripts got 99.99% of the job done and didn't give a false negative, so: "Ship it, junior."
And so we did.
However, the bug annoyed me because I had to got clean up the false positives when they fired. And, if I am anything, I am a VERY lazy programmer--and this was preventing me from being lazy.
So, I eventually am waiting for one of the other groups to deliver, and I go spelunking for a testcase.
Spelunking? HAH! Cave diving is a better analogy.
The program took the most inclusive of the syntaxes, used each record from that to build a regex to look for the corresponding record in the other syntaxes, matched what it could and flagged what remained.
So, the program was building a dynamic regex on-the-fly and then using it. Not a huge deal, but the regexes were larger than most people were probably comfortable with. No problem, I validated this on much smaller records out to REALLY big records, and they work well.
Except for those weird cases ...
So, I'm looking through a case that works and trying to compare it to a case that doesn't. And I accidentally fat-finger some character set match and delete a character that shouldn't matter.
And the regex fails ... provoking the Asimovean "That's funny ..."
So I delete another character ... and it works again. What?!?!?!
So, I add a character. And it fails. And I add another. And it works again.
I stared at that regex for what felt like EONS until the light bulb went on.
The one that worked? 511 characters or 513 characters. The one that failed? 512 characters.
So, yes, I, a total Perl n00b managed to find a bug in Perl 4 in my first ever Perl program.
Sometimes the junior dude gets really unlucky and finds an actual system-level bug.
Same thing happens in reverse! ;)
A few years back I told 3 devs who reported to me that there was a bug in Laravel database sub system.
The bug was: If you use the word "returning" in any Laravel insert query the system would crash (Laravel 3 & 4).
None of my guys would believe me!
I finally tracked the bug down to
> laravel/database/connection.php
> public function query($sql, $bindings = array()) > { > ... etc ... > elseif (stripos($sql, 'insert') === 0 and stripos($sql, 'returning') !== false)
I sent an email to the Laravel team and never got a response... but the bug stopped happening some time after that ;)
I think it was relate to mySQL version as well.
To the end user, what matters is that we solve their problem. We let them do their job, and we make that job as easy as possible. And that's what they pay us for.
And to the company we work for, what matters is that we solve the end user's problem, and that we do so in an expedient and economical manner, so that it costs less for us to do so than the customer pays us.
Of course, for us to continue doing this, in the long term, we need to innovate and architect and uphold standards and stay on top of technical debt. And all of that put together is "make the code good". But that's an end to the means, and the customer doesn't care what horrors awake when they click that dialog box button as long as the result they needed pops out at the end.
[0] about that, there seem to be a 'foot in the door' effect at play. If you get users with a good enough but imperfect product, they won't mind too much if you take responsibility for problems when they arise. Another instance of worse is better.
https://old.reddit.com/r/webdev/comments/bx51vk/we_need_to_t...
I was expecting to see some ill-advised use of WordPress in a critical software system, but no, it's about inconveniencing web developers. </rant>
never again.
Yes, there's backwards compatibility to maintain, but surely some of it can be contained, maybe with shims, like Windows does it.
In all likelihood, because the people maintaining Wordpress are the people who created it in the first place, and they don't know any better.
Why did PHP apps suffer from SQL injection attacks, long after PHP supported prepared statements? Why do Java apps often have ridiculous classes like AbstractSingletonThingFactoryFactory? Why does the Javascript community (still) promulgate useless leftpad-like packages? Why do Ruby apps often end up so heavily metaprogrammed that you can't trust any line of code to do what it says?
Cultures are hard to change. The people that are turned off by Wordpress' programming style probably pick other projects and other communities. Lots of the folks working on Wordpress probably cut their teeth in that codebase. They might not like it and they might realize there are better ways, but there probably isn't a critical mass of them that share a vision out of the morass.
Personally, I took one brief look under the covers of Wordpress and immediately decided to avoid it.
But it solves the problem users want solved. It's lego-level blog assembly, which is as technical as many users want to get. A much "better" system - maybe a nice type-safe Haskell static HTML templater with a command line build system - wouldn't.
Flipping this around, from the user's POV, "I wrote some code that almost does Thing X" is not a solution - it's just some code that doesn't quite do the job.
It's nice that it uses functional programming or formal methods or $shiny_language or ML or whatever. But if it doesn't do the job, it doesn't solve the problem.
That's what I think of when I hear technical debt.
What are examples that make sense and don't just explode in your face?
In your particular case, that MS Access DB was almost certainly the right choice when it was written, and the gotcha in that story is that they didn't start planning the migration away from Access until it was too late — effectively, they didn't pay off the tech debt when they should have, and got foreclosed on.
Our code wasn't built to be generic enough to be able to work on anything but Facebook. So what we did was simple: copy the whole project, change the stuff that didn't work right.
The next time a social networking site released a platform we refactored our main code and introduced the proper abstractions so we could use it on all 3 sites. That code ended up working for most of the other social networks that launched an app platform.
I don't think it's the type of debt that's the issue, as much as how you manage it.
I hate this term, because worse isn't better, it's worse. But good enough is good enough and technically-better-but-not-available-yet is worse than adequate-and-available-right-now.
Or they're a pro in a very different field than you.
When I worked in web development, I was gobsmacked at what professionals considered "good enough to ship". Now that I work in Medical EHR, the standards for 'good enough to ship' when lives could be on the line is very, very, very different.
I imagine a NASA engineer creating famously low-defect code or perhaps an engineer creating control systems for a nuclear power plant would similarly look at our medical code and think we too ship far too many hacks and defects.
Being a professional is knowing how to do your job well. Applying a single heuristic to every problem isn't that.
> Applying a single heuristic to every problem isn't that.
How ironic :)
And about that implied ad hominem. It doesn’t matter if I’m a pro.
Building codes allow one to say whether plans conform or do not conform to code. That way you don’t have to be a “pro” to determine whether the plan is up to code. You compare the plan to the code. You didn’t write the code. You don’t need to think the code describes beautiful plans. You just compare.
Somewhere there is a balance, but for business purposes it leans closer to the "working ugly hack" side.
One of the big lies of OO design is that you can manage that kind of complexity better with objects/classes, that you should factor out functionality into tiny pieces, and so on.
Unless you have written (and debugged!) an actual video game, you should spare your judgement.
Rules engines, statecharts and other forms of (declarative) behaviour modelling solve real problems.
But Modders also have a hard time adding features if logic for all items is in a single function. This seemed exactly what OOP / interfaces were designed for.
Also I'd love to see Terraria's actual source code. The de-compiled versions hat a ton of stuff like
> if (num1 != 109 && num1 != 110 && (num1 != 113 && num1 != 115) && (num1 != 116 && num1 != 117 && num1 != 118)) return;
or
> else if ((int) Main.tile[i, j].type == 19) Type = 94;
20x in a row, with obviously different numbers. Hope the actual code was more readable and just lost a lot in translation. But many more readable styles should be visible in IL (enums, constants, etc). There might also have been some obfuscation (can't remember), but couldn't have a strong one since names were preserved.
But yes, there are also advantages to their style of code. And in the end they delivered a product and that's all that counts.
There is no next release, maintenance, new features etc. Once Balloon Pirates is done it ships, and is never touched again.
There is also the side issue of game engines which many teams reuse from game to game so maybe that doesn't really apply.
If your code quality is bad, you can't do that efficiently. Your developers will hate their jobs and churn like crazy.
And by "bad quality" I don't mean "not unit tested" or "not commented" or "poorly formatted". I'm talking about code that is difficult to reason about, difficult to debug and difficult to refactor or extend. This is hard to quantify or even verbalize. It has to be felt.
The end user would rather have an application be frantically logging errors in the background, but be working for their usecase, than a program that is fixated with being correct and refusing to run in the presence of errors.
Obviously quality code still matters, but at least in the business realm "does it do what I expect?" is basically your success/fail state.
I'm not a coder, or a programmer. I'm a problem solver, and I tend to specialise in the sort of software infrastructure-y problems that are usually solved through code. If you think of yourself as a problem solver first and foremost, then you'll often realise that the best solution involves zero code.
This ${problem}-solver versus ${solution}-user gets even worse when people go beyond define themselves as technology-du-jour users, doesn't matter if it's Rails, React, blockchains, big data, ML, AI...
1. https://www.kalzumeus.com/2011/10/28/dont-call-yourself-a-pr...
I guess because I don't like any of the established terms.
Would be nice if there was a word that captures the idea of someone both designing and constructing the internals of a machine.
Engineers, as practitioners of engineering, are people who invent, design, analyse, build, and test machines, systems, structures and materials to fulfill objectives and requirements while considering the limitations imposed by practicality, regulation, safety, and cost.
If you’re doing that you’re an engineer. Hopefully we are all doing that.
You can do engineering without following any official standard, and anyone who do engineering is of course an engineer, so yeah, the protected title thing is just meh.
The big thing is that you need a P.E. in many cases to do things like sign off on drawings for regulators. Some civil engineers, mechanical engineers, etc. have PE's and many don't. In Louisiana, I had business cards with an engineering title and definitely worked as an engineer. At some point, had I remained in the oil business, I'd have gotten a PE because I'd presumably have eventually been in a position where I had approval authority over designs submitted to various government agencies.
ADDED: TIL apparently Texas is indeed exceptionally restrictive (in theory) about the use of the term "engineer." [1] I'd be pretty certain this is widely ignored in practice. Leave the oil business aside, I'm guessing that tech companies in Texas probably advertise engineering positions now and then. (Yep: https://jobs.dell.com/location/united-states-texas-round-roc...)
[1] https://www.statesman.com/news/20160903/their-name-on-the-li...
Go look into the actual law, you legally can't call yourself an engineer in Texas without a PE, or a couple tiny carveouts (on the order of you work at NASA, and NASA calls you an engineer)
There are doubtless tens of thousands of Texas job listings for "engineer" positions in software and elsewhere. (Software is especially notable only because my understanding is that PE's in software have basically been phased out. So you basically can't get licensed in that branch of engineering even if you have an accredited degree and have met the other requirements.)
Of course, there is something in the middle, and for software, that is probably “coders” or “programmers”.
The issue is that the problems that Bitcoin and Ethereum are trying to solve imply almost generating money from thin air. That gave unscrupulous people a lot of silly ideas, on the one hand, and, on the other, gave the people desperate to prove they're "thought leaders" (whatever that means) a lot of different, but still silly, ideas.
In both of these situations they're a programmer, but they sound cooler and will go a lot farther when applying for jobs or moving up in a company than the person who labels themselves as "a programmer".
"I'm a problem solver, I fix problems by diving down and welding broken things at oil platforms"
"I'm a problem solver, I make sure the books are correct at the end of the financial year"
etc. I mean it is correct but if someone calls themselves a coder/programmer/software engineer (even if they are not real engineers) then I know roughly what they are doing each day even if the value they create and what they are working on can be wildly different. Just like a deep sea welder.
In some ways I'd consider "software engineer" as equivalent to "novelist" or "journalist" where "programmer" maps to "writer" and "coder" corresponds to "typist". Software engineer, novelist, journalist all encapsulate a lot of responsibilities, where writer and programmer both talk just about primary means of achieving the job, and coder/typist bring it down to the mechanical skills you require to get the job done.
Requirements are pretty high: http://www.peo.on.ca/index.php/ci_id/2057/la_id/1.htm
But I suppose industry/capitalism loves the fact that we don't require licensing in order to produce software even if use of said software should be safeguarding health, property, economic interests, etc.
We are not engineers. We have no standardized certification process or tests. We have no (or very little) accountability. We have no codes of ethics. We may or may not be following proper, accepted development workflows. We may not even know what industry standard processes are.
I am a software developer because I solve problems primarily via software. This can and does include many different responsibilities and skillsets, but at the end of the day I primarily architect and write code.
It's up to you to educate the layperson as to what a software developer actually does. Although I'm not a writer, I understand that a "writer" doesn't literally only write. People can understand better than you're giving them credit for, I think.
that's your definition of engineering, or whatever officials that define it
more so, who cares about your skills other than your employer? And your employer cares about your skills, why should he care about your title?
even if other programmers who like to address themselves as software engineers, is it up to you to decide whether they can be hired?
It's just a title for god's sake
I would like to see the status quo changed, which is why I do discuss it. Of course it's fair that I hold an opinion and discuss it. There is no obligation for you to respond if you disagree :-)
> that's your definition of engineering, or whatever officials that define it
It's not my sole opinion:
> As with many other professions, the professional status and the actual practice of professional engineering is legally defined and protected by law in some jurisdictions. [3]
It's understood that if someone holds the title 'Engineer', they went through a certification process from a regulated body, traditionally. You can see this for example in countries and per state. [0] [1] [2]
> In Canada the designation "professional engineer" can only be used by licensed engineers and the practice of engineering is protected in law and strictly enforced in all provinces. [3]
In Canada (and I believe some states), it is illegal to sign off an email or other correspondence as a "Professional Engineer" if you're not actually licensed as such. [4]
---
> is it up to you to decide whether they can be hired?
When did I mention hiring? All I said was the term 'engineer' is loosely used in the software industry, and it has absolutely no standard around it.
> employer [...] why should he care about your title?
I wasn't talking about my employer at all. I only referenced skills because I was responding to a portion of the parent comment.
> It's just a title for god's sake
It isn't. How we frame something is very important in my opinion–just as important as the concept itself. [5] If it's "just a title", then people should have no problem calling themselves programmers or developers. However one can see that we call ourselves "engineers" because it sounds prestigious, despite the software industry being a total joke when it comes to standardization or even following basic modern practices consistently.
[0]: https://engineerscanada.ca/accreditation/about-accreditation
[1]: https://ncees.org/engineering/
[3]: https://en.wikipedia.org/wiki/Regulation_and_licensure_in_en...
[4]: http://www.occupationalhealthandsafetylaw.com/ontario-man-fi...
[5]: Consider for a moment the term "Global Warming" versus "Global Pollution Epidemic". I strongly believe if we had gone with the latter instead of the former, there would not have been pushback to the scale that we've seen. It certainly would have avoided the confusion of "oh, but this winter is so cold, global warming must be a hoax"! It also shifts the focus from an effect of pollution, to the pollution itself. This example is quite different than the software engineer/developer example, but I think it illustrates my point that how things are framed is very important.
We are not software engineers, and we won't be until regulatory bodies exist, and we develop codes of ethics.
I think you're misinterpreting the intention of the title of "Professional Engineer". As far as I can tell, it's for accountability for public projects (buildings, power, etc.). It's not strictly limiting what job titles can have the word "engineer" in it.
Most engineers in the aerospace industry don't even take the FE exam. Would you refuse to call most aerospace engineers "engineers" then?
Nobody said that you are software engineer, you can just call yourself whatever you want
Those who called themselves software engineers are indeed software engineers, no one can forbids it
Fancy term like engineer is for marketing purposes, just like you said it's prestigious, they use it because they want to impress people, anything wrong with that? No, it's correct.
And it's also correct if anyone think they don't fit the title engineer, because it's his/her opinion which the software engineers won't likely give a fuck.
No one can stop them from using the term.
As for the Canada's law, the earth is bigger than that AFAIK. China and US software engineers are waiting for arrest. Except for Texas FYI
Standardization/basic practices mean shits by the way, it's research and development phases during engineering, people can invent what they want in their own ways as long as their products are legit
From Wikipedia:
> Engineers, as practitioners of engineering, are people who invent, design, analyse, build, and test machines, systems, structures and materials to fulfill objectives and requirements while considering the limitations imposed by practicality, regulation, safety, and cost.
Wikipedia can be sue at anytime, as you like
Source: https://www.folklore.org/StoryView.py?story=Round_Rects_Are_...
At the same time, you have to actually describe your profession somehow. Everyone solves problems. Solving problems is definition of productive work. A carpenter solves problems by cutting pieces of wood apart and attaching them together. A bricklayer solves problems by stacking bricks on top of each other, with a bit of mortar in between to smooth them out. A car mechanic solves problems by fixing cars. A surgeon solves problems by cutting people open and fixing their insides. Eventually it turns into a bunch of meaningless MBA platitudes. "We don't 'build houses', we 'deliver solutions' to people's shelter-related problems."
If you go to a good surgeon with a problem that isn't going to be fixed with surgery, the surgeon is going to recommend a different solution and possibly even refer you to a different kind of professional. "Hey, you just need to rest that knee and maybe get some physical therapy. Here's a physical therapist I recommend." People understand that surgeons are smart and will occasionally listen to them, so surgeons can get away with this. More importantly, surgeons understand this. Surgeons don't go around thinking, "man, I need to perform even more surgeries because I'm a surgeon and that's what surgeons do" (or, at least, they shouldn't)--they have a deep appreciation of what surgery is and when it is or is not appropriate.
So I don't mind thinking of myself as a programmer, because I'm a programmer the same way a surgeon is a surgeon. If you come to me with a problem that cannot be solved by programming, I will tell you that, because to do otherwise would be a form of malpractice.
it's time for you guys to realize this effect from calling yourself "coders"/"programmers"/"problem solvers" are nothing but marketing
It's just to impress those who don't know otherwise
So basically a coder?
Can we stop with these simplistic maxims? Engineering is about tradeoffs and dealing with complexity. Trying to reduce the decision-making process to a single sentence is silly.
If the business problem is complicated, trying to further simplify the code usually makes it awkwardly abstracted and extra brittle. The fix should not be _just_ cleaning up the code, but rather trying to resolve the original usecase.
Its funny to see management types dismiss the programmers as being "airy-fairy" when they talk about things like code quality and technology stacks but then they wonder why things get completed on time, why bugs happen, why sites get hacked etc.
Somebody else has mentioned this analogy before in this thread, but if you had a house built would you say the same thing about the structural integrity? "As long as the floor can support me then thats all that matters", "House dwellers don't care what kind of structural beams are used to support the floor they just want it to not collapse". That kind of a mentality is ok for an average person living in a house but you'd you have to be off your rocker to hire a building manager who said that.
It does if you want someone other than the person who wrote it to be able to fix bugs and add features.
There is quality in code which you seem to be ignoring, troubling.
There is quality in delivering on time.
There is quality in the end-users' experience.
There is quality in the developers' experience.
There is quality in the investors' experience.
Quality is different than maximizing a metric (earnings per share, on time deliveries, user experience survey score, code coverage)
There is quality in finding the right balance in any situation.
Many many people want to maximize a single metric tacitly expecting everything else to be great, that is a good lesson to unlearn. Switching from one quality target to another is not.
If your problem domain is totally mapped out, maybe any solution that is quick to implement will work. If your problem domain is in any way nebulous or changing, well architected software will save you a lot of money responding to changes, and ongoing maintenance will obviously be cheaper if it's easily testable and robust.
But I think also the "code doesn't matter" really means, that ideally there is no (new) code, because there is in fact an existing solution but the person asking for the solution doesn't know it. This is likely more the case with internal stakeholders that ask for something to be built that does X, not realizing that there is readily available software or libraries that does X (or something close to it). So part of our role is to know the landscape of what part of the domain really needs new (potentially bug-ridden code) to be written.
I interned at a research facility where I had to figured out obscure serial commands for a two axis objective stage, interface microscope camera, and image analysis. Then I wrapped it all in a python library for them to use.
From my mentors perspective however, the goal was “simple”: you find a microorganism swimming under microscope, and keep following it to record its trajectory. Make sure each tick is max 10ms because those little ones move FAST. They didn’t care if I used 4 space indents or 2. They cared about getting pandas dataframe so they could do all the analysis.
I can’t explain this in a word or few, and I still don’t know what to call myself! Am I a developer? I don’t know.
I would say the title "Research Engineer" could fit quite well based on what you describe!
Code has to be maintained, or it will eventually fail as other moving parts around it change their interfaces.
Code rots. Platform norms are always changing, so today's fresh new code becomes tomorrow's smelly old code.
Code interacts with other code. As the volume of code increases linearly, the number of these interactions increases exponentially. Eventually the complexity becomes unmanageable and the whole system has to be rebuilt. The more code you add to a system, the faster you hasten its demise.
All of which means that code is a liability, in the balance-sheet sense of the term. Throwing code at a business problem hurts the bottom line. The goal therefore becomes to throw just enough code at the problem to solve it, and not one line more than that.
This is the difference between inexperienced and experienced developers. Inexperienced developers handle code like it's spackle. Experienced ones handle it like it's uranium.
All code we write is going to be wrong in some way as a result of our understanding of the problem domain being incomplete so I'd rather see code with an obvious place to put an if statement than a lovingly constructed masterpiece of indirection and abstraction.
Obviously there are basic rules to follow, make it testable, don't mix too many concerns in one place but I see so much bikeshedding and overcomplicated code when it just needs to not be obviously wrong and easy to replace when needed.
The software isn't necessarily what the customer values. You can provide a valuable service with 0 LOC
Yes if you boil it down to it’s essentials you are right the customer is the one with the money, so satisfying his needs/desires is what matters.
But, and thats a big but, we as devs are also humans, we also have desires and needs, and in the long run companies that successfully balance the needs of its users with the needs of its workforce get the best workers, which solve the problems of their users better/cheaper/faster.
So it’s more of an equilibrium kinda thing. If you stray too much in any direction the organization tanks - either the users loose faith, or the people satisfying theirs needs do, which leads to failure just as much.
The complexity with balancing this usually stems from the fact that both of those variables are subjective to their respective environment - users can tolerate bad solutions if there are no alternatives available, same with devs.
And that is also a subject to information availability - devs can be ok with their situation because they don’t know what salaries are at that other place for example.
I've been in a lot of code reviews where developers push back because it's "good enough". You need to maintain a defined level of quality otherwise codebases go to shit very, very fast.
I was recently told in a code review that a Cassandra read before a write (to ensure there were no duplicates) was "good enough" because a duplicate "probably wouldn't happen very often". Meanwhile, the consequences of a dupe would lead to a pretty bad customer experience.
I pushed back hard and forced the developer to rewrite his entire code. Would "good enough" be okay in this situation? My bar is much higher than this developer and I stand by my decision. We have the luxury of being tasked with solving customer problems and if we only strive for "good enough" every time instead of "the best I can do within the constraints I'm given", then in my opinion your career won't be very successful. We always have to make the best tradeoffs when it comes to time and expense, but the best developers are the ones that come up with the best solution and the best code that fits in a particular constraint.
My point was more about nitpicking line by line for perfection. What you're talking about sounds like a legitimate performance issue.
I think we're on the same page, but maybe my point wasn't clear enough. I tried to make it clear in my last point that "code is quality is important" but it's important not to confuse code quality with things that are more minor like idiosyncratic coding style.
Thanks for reading!
I probably won't like the variable names people chose but I won't comment on that because that's "my opinion". I will comment on even the smallest bug I see, because that's what we're paid to do. So line by line "perfection" is what I believe we need to strive for in terms of code quality. Maybe not so much "perfection" but "best practices" might be a better way of stating it. We always need to strive for best practices so that our code is predictably easy to maintain, read, etc.
And it's that level that we call "good enough". Or, I would say, acceptably bad.
One of the most important lessons I've learnt over my career is that there is no such thing as "good" software. Everything could always suck less — anything that takes over 0.0 seconds is bad, more than 0kB of memory is bad, more than 0 lines of code is bad. However, your level of badness for each of these metrics might be something you're willing to live with.
It's like hygiene. What you call "nice and clean" for your toilet is not clean enough that you'd cook on it, and even your "immaculate" kitchen is unacceptable for, say, an OR. Hygiene is always "bad", you're just looking for a point where it's no longer unacceptably bad for your purpose.
It's up to the team and customers to decide on that however. Database integrity is particularly important, so with limited information, I'd say you made the right decision. Therefore the first draft of the code was not good enough.
So, we should all be in agreement now, right?
> It's up to the team and customers to decide on that however.
You said yourself:
>the consequences of a dupe would lead to a pretty bad customer experience.
I've seen many situation were a duplicate wouldn't matter to a customer. It would matter to me because like you, I'm a perfectionist, but at the end of the day, it's both the team and customers that decide together.
Sometimes, good enough is good enough. If you were able to push back hard in this case, I take it you are senior to the other guy and your decision is/was justified by the product/feature requirements. IOW, in this case, good enough was in fact not good enough.
> constraint
most important thing, right there.
All software eventually gets rewritten. Either in full or in parts. So "good enough" means "will this keep it going until this software, or piece of this software, is thrown away and replaced". Because that is typically much more economical than code review infighting causes 2-4 rewites of every feature until its perfect. Or spending 3 times more time on a feature to make it perfect. Or having to hire very expensive developers that are capable of writing to that high standard.
There are obvious exceptions in specific industries, but this holds true for 80%.
Again without appropriate context, my bet here is that you guys are using Cassandra for other important features you won't get out of a typical RDBMS and as such you made a trade off to begin with and decided that Cassandra was "good enough".
Now the point I'm trying to illustrate (and I'm not just doing this to pick a fight, I promise), is that engineering is about trade offs and a big part of it is definitely related to likelihood of a problem occurring.
I think it also completely depends on the domain of the problem, the criticality of the process you're building and the outcome of a major failure of your assumptions.
So I'd just argue and say "good enough" is an entirely appropriate answer in many contexts and domains and it's important not to make a blanket assumption that it's wrong.
Way back in the stone age, MySQL did not yet have ACID transactions. They got them about the same time they stopped bragging about how much faster they were than Oracle, but I digress. Anyway, I had to write a bunch of transactional code around it. Drove the dba and me nuts. We begged for Sybase (we both knew it well), but the startup CTO was an open source purist and hated his first contact with the Sybase sales machine.
Eventually they folded, and the point was moot.
In fact Cassandra wasn't my first choice, but a strongly consistent database wasn't available to us. As I mentioned in another comment, making the very best decision you can given the constraints of your system is what one should strive for. Not stopping at "good enough" because of (poor) intuition that error conditions "probably won't happen".
We decided to go with LWT and eat the latency costs as a trade off to "stronger" consistency, realizing that Cassandra doesn't offer the same strong consistency as an ACID database. Not perfect, but it fit within our SLA, decreased the probability of encountering duplicate values, and if there was an error, it was easier to detect and the user could be directed to try again, vs having a completely silent error condition that would cause a small percentage of our users tremendous amounts of trouble.
I see a lot of posturing in this anecdote, what I think is missing are:
1 - an indication of how often would a customer experience the issue;
2 - how bad would his "bad experience" be;
Did you calculate the former and took in account the latter in forming your judgement?
Or, otherwise, was the "correct" solution simple and obvious enough that any non-junior developer would have picked that first without hesitation?
Generalizing here but assume 6 months later this rare duplicate happens for a very important customer so you can't just brush it off, you now really have to fix it. By then nobody remembers this code review so you don't even know if this duplicate is a one off rare event or if it is going to affect all customers. Fixing it in code review might have taken a few hours extra for one guy, now you sent the whole team scrambling weekend overtime just to find the issue and understand the implications.
That one hits home. I felt like I was being so helpful when I, fresh-eyed after finishing some entry-level C language book, suggested that we should implement unit tests to the legacy software.
The product lead, to his credit, did not chew me out, but stated very reasonably, "This software is old, relatively stable, and will likely be dead or sunset in five years. It's not worth the man-hours it would take to try retrofitting unit tests onto a piece of software this old, only for the product to be killed six months after we finish writing them."
I usually ask upfront in an interview if the team writes tests and if they don't and I get the idea they don't want to I probably wouldn't work there.
It doesn't matter if your work is amazing if nobody realizes it (unless you are fine being a starving artist, no judgement)
The corollary isn't that you should make everyone think your inadequate work is amazing. I don't think you personally are making that judgement but there are people who would.
Bingo. I used to be that guy who'd spend my weekends fixing the code that everyone else left messed up on Friday or at the bookstore reading up on the latest framework. And I thought that someday it would be recognized with promotions or more pay or even a pat on the back.
Nope.
> The relationship that you have with your boss ...
I should have been the guy who was always socializing with management at the office and happy hours. I should have been pushing my way into positions that were closer to the money itself - like getting contracts, attending conferences with management, architecture etc.
This is becoming even more apparent as I hit middle age, and it's harder to justify my high salary when the majority of our code isn't really all that complicated.
> and your bank account is what you need to focus on
But deep down I always knew this was true, so I lived frugally and saved most of my money. I honestly don't see a great future for guys like me who enjoy programming but are too introverted or just don't care to go into management positions. There is simply too many H1Bs, foreign competition, etc to justify high paid developers in most companies that aren't doing Google type development.
For a profession that has frequently professed meritocracy - it is certainly not. It's unfortunate.
Definitely goes under the heading of "Truths So True They Seem Obvious."
As for "Disorganized or messy code isn’t the same as technical debt," I can't look at messy code without automatically cleaning it up. It's automatic as I read and understand. In the last 5 years I've probably checked in hundreds of PR's of cleanup. If this takes anymore time and/or effort on my part I don't notice it. The only thing is someone has to merge it.
- poorly executed cleanup (aka regressions)
- misunderstanding of requirements (new bugs)
- "standardization" of naming (now you have two standards https://xkcd.com/927/)
- restructuring of code/renaming of abstractions which then adds friction to original authors of code (provided they are still around)
Left unchecked this can all happen, at the expense of feature work, and an unmerged PR resulting in missed deadlines and a frustrated junior. Alas we usually learn best by making mistakes though and I find this one hard to teach for some juniors.
I would argue that a measured tolerance of ugly code (and sometimes bad code) is more important until you learn how to spot and write code that doesn't look wrong.
Two articles I found helpful:
https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...
https://www.joelonsoftware.com/2000/04/06/things-you-should-...
In particular, it might make you think about game theory. Is the expectation value of this change more bugs, or less? Will making this change have at least a 50% chance of preventing a future bug?
As a Senior: Replacing all this architecture astronaut indirection with simple linear concrete code will solve all my problems.
After spending 7 million years (it sure seems like it) cleaning up the most vile garbage code you could possibly imagine, I'd like to elaborate on this:
Architecture is more important than nitpicking. While a small line of code could be improved, the stuff that tends to cause bigger problems down the line are usually architectural. I should’ve focused more on the structure of the application than tiny bits of code early on.
Architecture = the sum of all those seemingly unimportant "tiny bits of code".
It seems like every time I have to refactor or (heaven forbid) rewrite, I have to start deep down in those tiny bits. I've worked places with all these "genius" architects, but when I dive deep down into the code, I find a sewer than couldn't possibly support software life as we know it, no matter how brilliantly it was conceived.
Fellow programmers, you probably know exactly what I'm talking about, all those cancerous tiny bits that kill even the strongest patients:
- variables so horribly named that no one could ever interpret them
- 800 line iterations surely destined to be broken by a maintenance programmer
- conditionals so illogical that no one can tell if they ever worked
- early exits to hell that can only be fixed by rewriting
- double negative logic that could not never fail to break
- 8 lines of code where 1 could do
- <add your own>
Great architecture comes from both directions, "above" and "below". From my experience (unlearned as a junior developer :-) ), 90% of the problems have always seemed to come from below.Get good at the trees and the forest will flourish.
I've come to the conclusion good is good enough and working is even better. Usually the business agrees.
if (a) {
do_something();
return;
} else if (b) {
do_something();
if (something_else() == -1) {
return;
}
}
do_other_things();
maybe_exit_here();
maybe_keep_going();
it has similarity to frequent `goto label` type of codingOnce you have complex bail-out scenarios, the function needs breaking up or insanity follows... but that's true of any sufficiently confusing decision tree.
Generally I have found `else` to be something of an antipattern. I usually find it cleaner to write the code as 'this special early bail-out with a return, and the other case is just the rest of the function'. A bit like pattern-matching in Haskell, or a `switch` block in which the default case is the usual one. I also try to avoid reassigning variables, eg. in Typescript everything would be `const`.
But like all generalities, these have exceptions!
Often you end up in situations where all of the following are true:
1. Your teammate or colleague is designing or implementing something.
2. You have different ideas about how that thing should be done.
3. Your ideas would produce an objectively better system.
4. Even though (3) is true, the improvement to the system or design isn't worth the cost in your time, team velocity, team morale/development, etc.
In that case, the right move is not to intervene, and to let your teammate design/implement the system their way. This is hard to do, because most engineers are natural maximizers[1], but for most tasks you're much better off with a satisficing[2] approach. This isn't to say that you should never give feedback on designs or in code reviews - you absolutely should, but always remember that you have a limited budget for your own time and for team morale, and you should spend that budget on the feedback where it will make the biggest difference.
Looking back, I completely regret it. If I had my time again, I would stop after explaining the alternate solution. The time we would have had to spend fixing the problems with the original approach would have been worth it.
I've heard this repeated a lot, but I'm really only just starting to understand: Often the best leaders speak up less, rather than more.
Unfortunately, they do matter in the real world.
Titles count a lot inside an organisation, thought.
So what you need to do is be the tech lead for a team working on a bigger problem. Your job title doesn't have to change as long as you're working on a bigger job.
+1000 to this. Please don't be the guy that slows me down and prevents me from delivering features because you insist on everything being perfect. I can't tell you how many times and how much money people who are obsessed with code quality waste. They spend months polishing code only to get out a feature that no one uses and doesn't matter. But hey, the code is "perfect"!
This is a very true statement, especially concerning legacy codebases. I have worked on some projects that have had several developers make changes to it.
The original developers were great: they commented every class, had comments for all the methods, and added comments for any complex or funky logic.
Then the changes came. And the next developers hacked and slashed the existing code base to meet the new spec. Except they did not update any of the comments, so now what was once true and reliable is now frail and questionable.
Now, whenever I inherit legacy code riddled with comments, the first thing I do is delete all the comments. This helps me focus on what the code is actually doing, rather than what someone thrice-removed said it should be doing.
So I moved towards descriptive variable and function names, but those could lie as well.
So I'm thinking, how could we ensure truthful intentions at all?
And I think only a combination of small pull requests, good variable and function names and a thorough review can save us here.
But I don't know, I'm just a junior developer.
I think educating the developer / team on the existing codebase first would be the right step. Spend a day just going through the code. Learn how it connects, learn the full scope of the project. After this, make your changes in a way that fit into the existing code.
I've had too many offshore developers re-imnplement existing functionality, like reading data from an excel spreadsheet, simply because they did not look through the code that was already there. It cost them 2 hours of work to add another library and implement it, when the code they already needed was a simple using statement away.
In theory, this is why good orgs have senior devs: they know the codebases already. Furthermore, they should be reviewing these changesets to call out devs who mis-document or scatter about the code. But senior devs are people too, and they miss things. Additionally, some codebases are enormously complex, and it's virtually impossible for a single dev to understand it completely.
The catch is that you have to define way more types, but if the code is complex enough it's really worth it.
Deleting all the comments is too far in the extreme. How about just read them and realize they could be stale?
You can definitely over test, but how can you possibly know what you built works (or still works when you change it for the 50th time) if you have no tests? There's a trade-off with testing. Early in the dev cycle, not testing can make you go super fast (supposedly, this hasn't been my personal experience but in general it seems to be true for teams). But you'll plateau quickly and then at iteration 1+n you'll just come to a screeching halt because you introduce bugs or the new engineer isn't confident that they didn't break downstream things and needs to manually test everything. Testing early will cause you to go slower earlier (again, not my personal experience but seems to be generally true of teams) but you'll be significantly faster at iteration 1+n.
I usually summarize this as: testing early will on average make your development faster over the life of your codebase. Knowing this you can make trade-offs. Not testing early is probably better called prototyping. It's OK to prototype in production if you need to. But know what you're getting yourself into.
Because software development did exist before the invention of JUnit et al. The old fashioned way is 'user acceptance tests' and of course thats not perfect but its easily understandable, and nearly anyone can sit there and do it. And they might miss things or whatever yadda yadda but at the end of the day in an environment where everyone is short of time and time is money a bit of user testing can often be 'good enough'.
- the average tenure for a software developer at a company is 1.5 - 3 years, by the time things get that bad, its someone else’s problem.
- the business folks at a startup just care about throwing something together long enough to get their next round of funding or the exit.
- no one gets promoted by having code that is easy to maintain over the long term. They get promoted by releasing the new and shiny - not maintenance work. See Google.
How do you know what you built works? The only thing that truly matters is that the user story is satisfied and to that end unit tests are terrible. At best you could make some integration tests but that is not the same thing.
I also think there are better practices than tests to keep iteration pain low but that doesn't preclude tests so I guess that's not an argument against them. That said I prefer to focus on making composable, low side effect code than write more tests.
Personally I hate tests although I come from games where you need human QA testers to test that your game feels fun anyway so I do admit that's a unique situation.
In java land when I'm writing stuff i use unit tests mostly as a place where i can run code without having to compile the whole app. And any tests that come out of it just end up being a bit of regression protection. But there's always a bigger slightly more complex unit test i write which tests the bigger unit of function. And those really straddle the line between unit and integration testing but i find i get the most value from them because one you lock in and publish functionality you have a contract you have to honor.
I agree with the grandparent that they are more useful as a project matures rather than the design stage. Starting small and adding tests as designs solidify is a good tradeoff.
Even if you're just using a regular expression for parsing, it can be worth moving the parsing code to its own function and testing it like a parser.
Strongly disagree. Tests empower agile.
Here's a module/class/object/file. The unit tests test the externally visible behaviors of that code. Now stuff happens; the code needs to change. That's OK, we're agile, we can deal with it. I make the changes.
What did I break? I run the existing tests. Some break. Is that because the test doesn't reflect the new changes? I fix those tests. Or is it because I broke something? Good to know that early. I fix my code.
Next question: Did my changes do what they needed to do? For that, I write new tests.
Finding your problems early is a big part of agile. Tests are a big part of finding your problems early.
Now: If you're prototyping a new algorithm, would I write detailed unit tests before it gels? Probably not. (In XP, we called this a "spike" - trying to nail something down, not necessarily writing production code.) But even then, how do you know that your algorithm does what you need it to do? Maybe some tests?
The prediction is that the next time you revisit this code your original assumptions will still be valid. In games, which is my background, game rules (our business logic) are constantly changing during development to the point that you're fighting tests constantly for no benefit. Games are an extreme case but you can extrapolate the experience.
You've already admitted that there is a point when tests are not worthwhile when you are iterating. My argument is simply that in my experience you're in that state more often than not. Ultimately user value is the only thing that matters and tests don't predict that.
And I'm not saying you're not allowed to write tests. If something helps you do it. I'm arguing against code coverage and test enforcement.
No. The tests will encode the previous assumptions. When you run them, and they fail, either you broke something or one of the previous assumptions is no longer valid. But the tests give you an automated way of recognizing which assumptions you'd better think about, to see if they are still valid.
> Games are an extreme case but you can extrapolate the experience.
No - no more than you can extrapolate my experience, which is having core logic that is (mostly) valid a decade later.
> I'm arguing against code coverage and test enforcement.
Or at least against those things in areas that are constantly changing. Even in your world, though, are there areas that change more slowly than the game rules? Would it make sense to have tests for those areas, and not for the game rules?
I don’t know why people assume that tests hold you back from changes. If they do, they were just bad tests which were testing implementation details rather than businsss logic. That’s an argument to not write useless/stupid tests, not an argument to not write any tests at all. It just seems to be far more difficult for developers to decide what a good test should look like than to decide what any other good code should look like.
I was upset, because people that seemed to be contributing less and were less-qualified (at least from my admittedly-biased perspective at the time) were promoted to a higher level than me, and I got a fairly form-letter-esque answer of "we don't have the budget to promote you this time".
The next cycle, they corrected it, and I was officially a "senior engineer" on paper, and I realized how silly my hissy-fit had been. Sure, I guess having a bit more money was nice, but it's not like it radically changed the quality of my life, it's not like having a fancy title changed how people really saw me, and I didn't even bother updating the title on LinkedIn.
I don't think I was "wrong" in what I said. I do think that I deserved the promotion over someone else, but at the same time, I also tarnished a relationship with my boss and coworkers, and I let it get me far more depressed than it should have.
-------
I guess if any "junior" engineers are reading this, try and remember that a title is simply that: a title. They don't matter a lot, try to not get too upset over them, and obsessing over something so nominal is a great way to build up anxiety.
EDIT: Just a note, I absolutely think you should call out a company if you feel like you're being taken for granted. I'm not advocating complacency, just make sure that your hatred is directed to the right places and try to avoid getting too depressed.
The issue was the they had a limited budget for promotions, and basically limited it to one person per team. This, by itself wouldn't have bothered me too much, since the person who deserved it most on my team (someone with more experience than me, and was definitely under-leveled) did get promoted that cycle.
What upset me most was that a person on another team (with 1/3 of my experience, with no increased education, and on a team that accomplished nothing (not just my opinion, that team was disbanded a year later)) got promoted to a level higher than me. My direct boss didn't have any control on that team.
After about 10 years, I just started calling myself a 'senior software engineer' on my resume and nobody has ever called me out on it.
Must be a Silicon Valley thing tied to salary. But in the areas I've worked, titles don't seem to exist for non-management.
* "Oh we could do that, but we're on python 2.7"
* "Oh we could do that, but we're using Java"
* "Oh we could do that, but we're using a relational database"
2. To write good software, we cannot just be a coder, we need to understand the business and the project management
3. There are many seniors became senior because of their age not their ability. I am not talking about using a particular tech, I am talking about their mind. Many of them still think like junior even they are at a senior position, the way they are working didn’t scale at all.
4. Fundamentals is very important. https://hackernoon.com/the-doctor-and-the-scalpel-78656f508c...
Just to comment on one part at random:
Code reviews would be more useful if review feedback was categorized:
1. is this a matter of personal style? 2. is this a judgement call? 3. is this something that is just wrong and has to be fixed? 4. is this in-scope or actually a separate issue?
(This is off the top of my head, so this is more of an example of the kind of categorization I'm talking about than a proposal.)
It's useful because it helps to set the direction and expectations on what to do about an item of feedback.
E.g. if you have a lot of 1. then the right "fix" might be to spin off a task to develop a common style-guide. (Or maybe fix an out-of-date a style guide, or to enforce an existing style-guide so that these issues don't dominate code reviews, etc.). For 4. the resolution would be to open a ticket (or whatever the process is so it gets proper consideration, prioritization, etc.)
Where I am currently we spend a lot of effort figuring out what to do about review feedback (and I think we too often make non-optimal decisions which sucks time as well).
My team and I are kind of trying to break this up, since we suffer from this a lot.
The Simplest way is usually the right way.
What I mean is that fancy algorithms, sweeping design patterns, and "clever" pieces of code are generally not the best approach to 99% of coding specific problems.
Example: Nested for-loops. Generally a bad choice. When encountering a nested for-loop, one may be tempted to try and refactor it into some sort of recursive and highly-performant function with O(n) complexity, etc. You go through all that work and then realize that the most iterations that for loop will ever see is ~10. You just wasted a ton of time writing code that is more complex, harder to debug, and generally more opaque.
In my experience, I've seen this a lot in the context of premature optimization. I've also been the perpetrator many times as well.
YMMV depending upon region and company.
1: "It's good practice"
For example: Wrapping every function implementation in a memoize function may seem like a good idea (= it prevents doing unnecessary work-heavy stuff), but it actually makes code both harder to read (more boilerplate to skip when reading) and most cases slower (= even when a function is executed only once, you're doing extra checks and function call).
2. "We're gonna need this soon"
For example: many times I've implemented an abstraction that makes sense for sharing code with a feature that I know we'll be implementing soon, only to find out priorities have changed and that other feature never gets implemented. What's left is an unnecessary abstraction that only makes the code harder to read and maintain.
3: Optimising for speed/lines of code
Unless you're building a game engine, it rarely makes bang for the buck to optimise for speed until you have identified an actual bottleneck in performance.
Same for one-liners. It may be cool that you know how to write 10-line function as one-liner nested ternary, but your "clever" code is probably less readable and harder to maintain.
Now as a junior I just wrote how I liked and hated everyone else's code. Then came a long awkward phase where I tried to fit in closely with other people's styles and figure out their intent and design - this is a very difficult way to write code and I wasn't very productive. I thought about this a lot and now I just go ahead and blaze a trail and other people can just figure out my style. Move fast and break things - or being reckless, its often hard to tell, but you wont be a 10x dev by trying to be nice and fit it.
My first gig was at a 3 person team, including me. The other two wrote their code in basically identical format. I ended up codifying a linter config to make a style guide that basically all existing code already fit into. I remember thinking, this is never going to be so easy again.
Your post makes me think I was right.
Getting code reviews from other people that understand this, or pair programming with them, is the best way to practice empathy for those future code readers (which very well may be you).
Don't focus on commenting about how. They can read the code for that, and those comments almost always drift from truth. Instead, focus on writing code and comments that describe the why. Consider describing what other approaches you tried, why you didn't use them, and why you went with the current implementation.
This especially applies if your solution may not be the obvious first answer.
Try to help the Engineers of Tomorrow from repeating prior mistakes. Free them up to make new and grander mistakes, instead.
When you have to support legacy code with no docs, no specs, no tests, you change your mind pretty about the value of tests and a good specification.
As a senior engineering manager, I push hard to get good requirements for my team. Its being a sales person with the business side.
I think a healthy mix of idealists and realists is necessary.. With senior developers representing a good majority of the former.
I think your company is suffering from bad architecture design more than anything else. It seems like everything is tightly coupled with little room for modifications. This probably means those senior developers weren't really senior to begin with. This tends to happen at startups where business is prioritized and people get hired with inflated titles. I have seen that happen in a lot of startups.
But you are right! The company is suffering from horrible architecture.. And any attempts to change it are near impossible because of the sheer amount of code and processes that we have. It seems like a never ending battle
I think developers will still make good money for decades. I could, however, see an issue with junior developers finding less opportunities in the coming decade. I think there will be less need for juniors and the entry level jobs will be harder to come by. Some of those skills are more easily "commoditized."
The entire idea of a factory worker is that they can be swapped in place by another and the output is maintained to a great extent. This just simply isn't the case in software or really any creative work. Even if we assume two workers have equal skill sets, something that is already dubious due to the importance of cross-domain knowledge in software, the personality one brings to a team will ultimately alter the entire team, sometimes good, sometimes not so. The social dynamic within groups is a critical element and something that cannot be commoditized away like other professions.
As to the point on commoditization within old tech, I can only answer with a "well duh, It's tech!". The goalposts are always moving within this profession. There used to be a whole heck of a lot of jobs in programming super low-level tasks, those are largely gone and have been replaced with new positions. Last year everyone worked on web apps, this year everyone was on mobile apps, next year it's anybody's guess.
I spend a lot of time within WordPress ecosystem and I've seen people coming to me with 20K products shops they've built and maintain without a single line of code written.
I've seen companies operating online without having any developments either on contract or in house.
Because for certain simpler scenarios, there's UI based tools you can run your business on.
Sure they come to me because eventually they need something they can't do with tools they use but those are becoming more and more high level jobs.
Web development is pretty much figured out problem. You have legions of repleacable low value developers churning out pages and you have subset of highly paid specialists who can do more advanced things.
But those gazillions web pages are doing their job, they drive sales, improving information quality etc. so I could say digital workers are factory workers of our time.
On certain jobs line is blurry - designers can do basic WordPress but they also can set up MailChimp because machine needs to be humming.
My senior thought: spend 1 day to SOLVE the problem, and 5 minutes removing the code entirely.
In the first case, I spend most of my time in architectural design and modelization. The implementation phase is rather quick, because everything is well defined and you can write a lot of tests upfront.
In the second case, you’ll discover the big picture by facing the problems while trying to find and implement a working solution.
While getting experienced means how fast can you figure out the big picture.
Sometimes it's about making things less complex, which means good practices, code quality, clean code and testing means you can afford a bit more risk.
Equally, sometimes you employed to just get things done, make it work, even fake it till you make it, proof of concepts, minimum viable products, etc.
I've done both, I enjoy both, but they are two different modes of working.
Ultimately the answer is "it depends" and milage may vary.
Quality is a journey not a destination.
Yes, put delivering value above anything else, sometimes that value is quality.
When I was younger, I liked clever solutions to show off my skills. Now I cringe at that. Other people have to read my code. And more importantly, “I” have to read my code tomorrow or next week. I’ve started to really prioritize simplicity. Which is ironically quite hard!
>Imagine 50+ comments on your PR with all the semicolons you missed!
Depends. You should have a style guide and it should document what the expectation is. If it was decided that semicolons are mandatory, then code review should be failed. If they are optional than the code reviewer shouldn't flag it. The alternative is to use a linter/formatter and either auto-fail during a pull-request or have the tool fix it up.
On my current project I have 100% test code coverage. Which I believe is quite unusual. But I am pretty sure that if I give a talk about how I did it, most people will be horrified.
For me, becoming a senior developer has allowed me to see code more objectively than before. Instead of following convention to the T, I can now look at code and make my own judgement whether to follow the convention. It's great to have the sense of relieve.
This. So much this. I didn't realise that until a little while ago but when I did it made me realise I need to understand actually what make someone a senior. It's not just an automatic progression.
- Assumptions are the mother of all fuckups.
- The optimal number of people in an organisation is 3. At 4 you start losing efficiency. At 80,000....
- The more you progress in the hierarchy, the more you realise it is the same idiots at every level.
This one hit strongly for me because I didn't realize it until reading this...
I am sure our CTO and PM are tired of me pushing for the latest tech I read about on here all the time. I will hold back a little more from now on.
Yeah, don’t take the “orange website” too seriously.
It's much better to get something, anything, on the screen and working than an elegant design constantly refined but never put to actual use.
The truth: "It's not about the code, it's about the people."
it's up to us as technical professionals to care about both the "what" and the "how". the ability to pan in and out on a particular need was learned early on and i cannot be more thankful of that.
The biggest problem with "technical people" is the desire to retreat to a pair of headphones and not communicate with anyone all day. Set aside some consistent portion of your day where that's the only time you'll take meetings and we'll work around your schedule.
Technical and nontechnical people should collaborate face-to-face on a daily basis. That includes customer interactions.
And that's the overwhelming majority of ways to make money with software. Think of every video game, desktop application, and mobile app that people purchase. What you call "bad boundary" isn't bad at all. The customers just expect the software to do 2^10000 combinations of possible things across N platforms on M hardware and integration with P external services. Imagine if you emailed a company's tech support saying that the CAD software you bought doesn't work with your Nvidia 1060, and they responded "sorry, that makes our sybsystems overcoupled".
Spot on!!!
If I were to step back and try to characterize the growth in my understanding of software development in a single general statement, putting aside all the hard-tack technical experience, it's this:
I am beginning to understand the subtle and complex delineation between _good_ and _useful_, and the role that execution plays in that - with all it's myriad parts: prioritization, management, technical risk mitigation and hypothesis validation, consensus building on technical direction, post-hoc validation, etc. etc.
And usefulness has a lot of facets in execution: not just technical but social. A good project and good idea might come to nothing if you are oblivious to obtaining some organizational mandate for it, and it gets sidelined due to shifts in priorities. If this happens, it means a misstep was made earlier: either the project should never have been started at all - due to awareness of upcoming priority changes, or you should have done the organizational consensus building to ensure that the work had the runway it needed to complete.
The same goes for technical consensus among implementors. Sometimes this can be avoided by giving clear mandates to trusted individual leads, but come complex projects really need the input of multiple senior members.
I'm starting to find lately that many of the contributions I'm most proud of are the ones where I come to firm conclusions on what work _not_ to do: concluding that certain tasks that were good but not useful enough, or determining ahead of time that certain planned implementation paths are actually not going to deliver what we might have expected them to, and thus we should scrap that idea. It has saved inordinate amounts of time that would _otherwise_ have been wasted.
The challenge for me has been reconciling this new understanding with my old methods for evaluating my own performance. I know these days, within reasonable bounds of arrogance, how to write decently complex software and understand it. As I move on to considering these higher level concerns, I find that I'm asking myself whether that opportunity cost is worth it: "I _could_ be spending time writing good code right now, instead of analysis and reports and meetings and planning. Is this new activity of mine _useful_?"
I'm still building my internal model for measuring my effectiveness in this new domain.. but it's clear that it's a profoundly impactful and worthwhile multi-factor optimization problem to tackle.
Is she talking about HN?
1: A great quote from one of the most senior developers I look up to town said this: a developer who's been developing for 1 year can have _far_ more experience than someone who's been doing it for 3 years. I find people who introduce themselves as "developer for 15 years" aren't those types, rather it's the ones that show innovation.
Another quote is this - to become a better developer, you need people at the same level as you, more experienced, and less experienced. So I've taken it upon myself to mentor junior developers in town. Other ways I've learned is by talking about technical challenges from multiple developers in multiple companies in my surrounding region, doing technical & lightning talks, and participating in hackathons.
2: I wasn't entirely surprised by how little testing there was even at companies that have been around for a 3+ years, I had learn that most companies in my area (even the ones that have great developers) - code test coverage was poor. There is an opportunity cost in testing something that might change later. As kent dodds put it, do some tests coverage on core features, but have integration tests
3: I think working with legacy codebases is a great way to learn. The codebase shows a story about how the scope of the project changed, how workarounds were made to meet client expectations in short turn around time, etc. These things you don't learn on your own. Being forced to break something down and build it into a better version is rewarding, so long as it's not everything you do.
4: My first code review was more informal, it was more pointing out everything I didn't consider when implementing a feature. E.g deleting old unused code, formatting things semantically and expressively, etc. Was still setting up my dev env so there were a few grammatical errors that didn't match eslint styleguides, but we had test runners for those.
5: Documentation to me has always been important, but forcing it upon others as a way to bookmark something I avoided. I think it's far better to just notate on a piece of paper your interpretation of how the code works, or write your own documents in Confluence/team-wikis so your senior dev can understand your interpretation of how things work. As the new dev on my team in a year, documentation was out of date and each dev only knew specific parts of the codebase, so I've been updating how everything works from a eagles-eye perspective. Those notes have a lot of value to the next dev that gets hired, because they come from a clean slate like yourself.
I also think, documentating your pseudo-code inside of tickets is important. I've learned the hard way from getting fired at a previous job many years back - you need to actively show your team what you do. Documentation is the lowest hanging fruit, you need to think about what you're going to say in your daily standup tomorrow, communication is essential
6: For me technical debt is really being able to identify where the debt lies. For instance, rails and laravel has well documented magic, but the path less traveled needs to be understood. My rule of thumb is if I'm going to write less-than-ideal code, it needs a comment block indicating why I did so. Don't push code you yourself don't want to read 6 months later
7: In regards to seniority, I think the best developers know they are junior in other areas and are multidisciplined in other fields besides programming. I've learned a lot of great wisdom from developers in my city's slack channel just by seeing conversations unfold. I know some first year developers whom I already consider senior, and some developers who've been doing this longer who I do not.
I think the most important thing I've learned in these 2 weeks in my first dev role, you can be a mediocre dev who just copypastes things and still be a great asset to any team. There's a lot of value in just documentation & communication alone
Other things I learned - you can bother your senior dev if they just sat-down or stoodup from their desk, or if their headphones are off. But experiment with communicating asynchronously in slack and synchronously in person and see what works and what doesn't - even if they sit right next to you.
At the crux of everything, it's important to have a certain growth mindset. I embrace a japanese methodology called LEAN. Other things - being okay with self-humilation is a great way to learn. Other things - force yourself to take breaks by only using small water bottles or none at all.
Also, make sure you say hello to everyone in the morning, my office is 20+ mechanical/electrical/civil engineers/designers, I make sure to say hi to them. I take the longer-route of walking through the frontdoor every morning, my boss makes fun of me for that. Give the gift of giving to others in the community, it reflects well on yourself, my team already had heard of me despite never meeting me b/c of how active I am in the local tech community. Rubber duckies are good inexpensive programmer gifts.
In fact, I think there should be something like a documentation driven development, where first devs change the description of what code should do, and then change or add the tests, and then finally write the code.