What senior developers can learn from beginners

Over the last couple years, I’ve had the luxury of working with and mentoring quite a few beginners. While I’ve obviously witnessed my fair share of programming no-no’s, things are not as black and white as they may seem. There’s a handful of patterns and behaviors I’ve seen consistently throughout beginners. And while some of these behaviors are misguided and detrimental, many present a learning opportunity to senior developers. Contrary to popular belief, there’s quite a few lessons that can be learned from those with less experience, as they have a bias-less perspective (beginner’s mind). 

Conditional inversion


One of the most common anti-patterns I’ve seen with beginners is something I like to call “conditional inversion” (it might already exist with a better name). Conditional inversion can happen when using branching statements to control or limit the flow of code. Below is an example of the inverted code:

function atleastOneOdd(potentialOdds) {
  if (potentialOdds !== undefined) {
    if (potentialOdds.length) {
      return potentialOdds.some((num) => num & 1);
    }
  }
  return false; 
}

exaggerated for effect (but I’ve definitely seen worse)

If you think the above code looks far too complex for such a simple task, you’re not alone. The perceived complexity is a result of nesting, as nesting strongly contributes to the “perceived complexity” of code. Here’s a functionally equivalent but far more readable implementation:

function atLeastOneOdd(potentialOdds) {
  if (potentialOdds === undefined) {
    return false;
  }
  if (!potentialOdds.length) {
    return false;
  }
  return potentialOdds.some((num) => num & 1);
}

The lesson:

In general, less nesting makes code easier to read and maintain. As with all rules there are always exceptions, so make sure to understand the context before making a decision. Here’s a great thread which covers this specific scenario in more depth:

https://softwareengineering.stackexchange.com/questions/18454/should-i-return-from-a-function-early-or-use-an-if-statement

Knowing when to look

It’s important to remember that it’s not just that beginners know “less,” it’s also that they haven’t yet developed an instinct for what they should expect. After a certain number of hours spent coding (not literally), you develop a keen sense for what granularity of logic you can expect to be available from standard libraries and packages. A great example of this is a situation that came up with my girlfriend. My girlfriend is a computer science student, and I was recently giving her some feedback on code she wrote for a C++ assignment. As part of the assignment she needed to check if a given input char was a letter. This was roughly her implementation:

bool isLetter(const char someChar) {
  const char letters [] = {
    ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’,
    ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’
  };

  int i = 0;
  for (i = 0; i < 26; ++i) {
    if (letters[i] === someChar) {
      return true;
    }
  }
  return false;
} 

I couldn’t even be mad. It’s such a direct and logical implementation. But as an experienced developer, instinct tells you that there has to be a solution for a problem this common. In fact, my immediate instinct was to take advantage of the numerical nature of c chars and simply check if an input char is within a specific ASCII a-z range. But due to my “programming intuition” I suspected that was still too much work for such a common problem. I told her to Google if there is a way to check if char is a letter. Turns out there is—isalpha—which I’ve probably used a hundred times but don’t remember (something that happens with enough programming—you can’t remember everything).

The lesson:

Developing an intuition for whether something already exists that solves your problem is critical for succeeding as a developer. For better and for worse, the only way to strengthen that intuition is to write more code in a diverse set of languages and paradigms.

Finding the most direct solution to a problem

Beginners are absolutely amazing at finding direct or literal solutions to a problem. Just take the isLetter example from the previous section. While my girlfriend’s solution is definitely not the most efficient (memory or compute wise), it’s very clean and gets the job done. While the tendency to gravitate towards the most brute force solution can be costly, most of the time it doesn’t actually matter. I’m not saying that you should throw performance to the wind and only rely on brute force solutions, but there is a lesson to be learned here. To understand that lesson, it’s important to look at one of the key differences between beginners and experts: what they’re trying to achieve. 

A beginner is usually programming to learn how to program, while an experienced developer is usually programming as a means to achieve an end (work, personal project etc). In college, a computer science teacher will rarely give an assignment that has a performance requirement. This results in students learning programming without performance in mind. At first this may seem like a huge deficit in the way that we teach people programming, but I actually think it’s one of the few things that higher-level education does correctly. Worrying about performance is generally a symptom of a very deadly disease known as “premature optimization.” From my observations, once a programmer starts worrying about performance, it’s very hard for them to ever stop. 

This wouldn’t be a problem if optimizing for performance didn’t take extra time, but it does. Combine that with the fact that performance almost never matters (this is coming from a low-level C/x86 programmer), and it’s clear why always optimizing for performance can be problematic. This effect is so potent, that I’ve actually seen beginners arrive at a working solution faster than a very capable and experienced developer simply because the beginner only cared about getting it working and the senior developer assumed that it had to perform well. The irony being that even after the senior finished the “performant” solution, you couldn’t tell the difference between theirs and the beginners.

The lesson:


The direct naive solution is usually enough, aka Occam’s Razorish

/** I’m assuming a lot of readers will take issue with this section because it may seem like I’m encouraging people to write bad code. That’s absolutely not the intention, and if anything, I think more time should be spent making code readable and maintainable and less time optimizing the performance. */

Everything has to be a class

One of the more frustrating behaviors I’ve seen consistently in beginners is the inability to code outside of the class paradigm. I blame this one on every college that only introduces students to Java in computer science programs. I’m generally language agnostic, but I always found the “everything has to be a class” requirement to be a particularly silly aspect of Java. We get it, somebody at Sun really liked OOP. The price for that obsession is that 20 years later, Java is still trying to reassemble itself into a language people actually want to use.

To understand why this is a real issue and not just a complaint about Java, we have to think about how beginners learn. Beginners tend to internalize concepts at the granularity they are presented.  So when they use languages such as Java, which enforce the class paradigm, they tend to develop the understanding that the minimal unit of code is a class and the expectation that every variable acts like an object. Using another object oriented language such as C++ will quickly prove that the minimal unit of code does not have to be a class—it’s often overkill and cumbersome to write an entire class for primitive and stateless logic. Don’t get me wrong, there are tons of real life situations where these semantics and guarantees are desirable—in fact, it’s the reason Java is one of the most ubiquitous languages.

The problem is that these semantics paint a misleading picture for first-time programmers. When you zoom-out of the Java world, you’ll notice that very few programming languages are as object-oriented. This is why it’s so dangerous to learn programming with only one language. You don’t end up learning programming, you end up learning a language. 

When I’ve discussed this belief with other developers, they often argue that these downsides are actually upsides because Java avoids having beginners shoot themselves in the foot. While that might be a great rationale for a Fortune 100 company that needs to hire a lot of untrusted programmers, we should not introduce programming to beginners like this.

The lesson:

The ability to generalize things is one of the most important tools you have as a developer. Starting out by learning a single language is completely understandable, but it’s absolutely crucial that you quickly branch out and diversify your understanding. Doing so will transform you from a pretty-good <Insert language here> programmer to a great developer.

Strong syntax and structure

Beginners tend to rely on consistent syntax and structure far more than experienced developers. This actually makes a lot of sense once you think about it, considering that humans are pattern-driven and code syntax and semantics is one of the most explicit examples of patterns. For an experienced developer, understanding what a function does (and how it operates) is usually a matter of going to its definition and reading the source code. It may seem impossible to imagine not operating in this fashion, but I almost guarantee that even the best developers didn’t start out this way. Pattern-matching is so fundamental to the way humans learn, that it extends far outside of programming. For example, English is notoriously one of the hardest languages to learn. When would-be English learners are asked what makes learning English so difficult compared to other languages, they usually point at the inconsistency and lack of reliable language rules.

/** FTR: One of the best arguments about Java for beginners is its strong and consistent semantics */

A practical programming example of this happened when I was recently helping a beginner write a C++ class that had a relatively complex internal state. Based on the goals/intended usage of the class, it made sense to have some of the methods directly mutate the state while others returned a copy. After writing quite a few of the methods together, I left the beginner to write the rest by themselves. When I returned later on, my student had not made much progress and communicated that they were stuck. Specifically, they had become confused about which methods were mutating the state and which methods weren’t. This is a situation that an experienced developer does not encounter, as they will simply look at the internal logic of the method and determine if state is being mutated. But a beginner is not yet fluent enough to quickly parse that same logic (even if they previously wrote it), and instead relies on the syntax and structure of the code. After reviewing the code, I realized that their struggle was partially my fault.

When we implemented the initial methods together, I made sure there was a good mix of mutable and immutable. What I hadn’t realized is that those methods presented a misleading pattern to a beginner. Specifically, every mutable method was a void function, and every immutable method had a return type. 

class MyType {
  void addElem(int elem);
  MyType createCopy();
  ...
};

I had unintentionally taught my student a pattern that obviously is not true in practice. So the moment they needed to implement the mutable bool removeElem(int elem) or the immutable void printElems(), things fell apart. I say that this was my fault because at the end of the day, I was lazy. As an experienced developer, I didn’t rely on syntax and structure as much as the actual logic I was actually implementing. This is silly because the code I wrote could be improved as to leave zero ambiguity for beginners and remove the potential for bugs at nearly zero cost to me. How did I accomplish this? Through the use of the const keyword, which allows me to explicitly indicate if a method has the potential to mutate state:

class MyType {
  void addElem(int elem);
  bool removeElem(int elem);
  void printElems() const;
  MyType createCopy() const;
};

The lesson:

It’s really easy to forget how other people might interpret the code you write. Stay consistent and utilize language capabilities that enable you to write digestible and explicit code. 

Conclusion

Many of the initial patterns you embrace and adopt as a beginner will fall to the wayside as you’re introduced to more efficient and maintainable solutions. That being said, I hope this article illustrated that sometimes even the most experienced developers can benefit from a bit of “unlearning.” While beginners may not be as comfortable with advanced features and language idiosyncrasies, sometimes that’s a gift when the job just needs to get done.

Author

Ryland Goldstein
Product Guy
After becoming interested in programming at an early age, Ryland fell in love with complex system design. His first experiments in programming concerned game design, which lead to the eventual creation of his own game engine. His primary strengths are distributed architecture and scale out machine learning. Before coming to Binaris, he lead teams responsible for implementing deep and classical learning algorithms on an in-house high-performance compute runtime. Today, Ryland is now a driving force behind Binaris's newest product, Reshuffle, which he partially incepted.

Related Articles

Comments

  1. Regarding your initial refactoring: I agree that the “early return” form is easier to read, but the check for length is still inverted and treats a number as a bool. `if (potentialOdds.length == 0)` expresses the condition in its non-inverted, easy-to-read form.

    But… why is there a length check at all? “some” is documented as returning false if given an empty sequence because it is false that any predicate is true of some element of an empty sequence. Eliminate the redundant check entirely, don’t reformat it into an easier-to-read form!

  2. Leaving aside those small points, I agree with the general thrust of the article. I frequently leave comments on questions from novice programmers asking them to please explain to me the thought process that got them into the hole they are trying to get out of, and it is almost always interesting.

    A common theme is “I was trying to do X, so I did Y, which didn’t work, but I discovered by trial and error that if I modify it like this crazy thing it works right sometimes but I don’t understand why”. The notion that programming can be *principled* — that we proceed by understanding the abstractions afforded by the language, and then match those abstractions to a model of the business domain of the program — is apparently never taught to a great many programmers. Rather, many programmers proceed as though they’re exploring an undiscovered country, and going down paths more or less at random and hoping they end up somewhere good, no matter how twisted the path is that gets them there.

    Designing tools and languages that support user success when both principled and unprincipled approaches are in use is quite a challenge.

    1. The code sample for the first section is definitely non-sense. In my defense I did put

      > exaggerated for effect (but I’ve definitely seen worse)

      below the code snippet.

      Overall I think your insights are really accurate. This part especially resonated with me:

      > The notion that programming can be *principled* — that we proceed by understanding the abstractions afforded by the language, and then match those abstractions to a model of the business domain of the program — is apparently never taught to a great many programmers

      Thanks for the genuine feedback glad you liked the article.

    2. Maybe this is why unit testing became so fashionable – unlike a language such as C, modern languages are so convoluted that its next to impossible to understand quite what going on under the covers. So you make best guess (or copy paste from SO) and as long as it passes the tests, that’s all you care about.

      We’re no longer computer scientists instructing a computer system to perform correctly, we’re craftsmen banging things together in approved patterns without much knowledge of why it works.

      A C programmer will always be taught memory and abstractions like pointers. A C# programmer will be taught “do this and magically that happens”. I guess this was by design, trying to make programming much more accessible, and easier, and thus cheaper.

      We’ve abstracted the computer system away so much with these that there’s little point in trying to teach any link between the language and the hardware any more.

  3. Your first example irks me quite a bit. You’re right that nesting should be removed when its absence could make the code cleaner and easier to read, but you should also remove unneeded/redundant checks. Still, it was a good read. This makes me ponder about how similar teaching newbie programmers and kids is similar – if your youngling sees you putting raw cake in the oven, and taking it out as a baked cake, it may connect that Raw Food + Oven = Prepared Food, but might miss up in the nuances that not everything that you want to cook goes into the oven, nor that you should actually _turn the oven on_ if you want to actually cook something on it.

    1. I knew the code was convoluted when I wrote it and I should have changed it. At the time I didn’t think the specifics of the code itself would matter (outside of the aspects I was targeting in the section). Maybe I’ll sneak an update later today and make the code more sensical.

      Thank you for the honesty, appreciated reading your response.

      1. To be fair, I’ve run across code like that (and worse!) and had to refactor it so it was more easily maintainable. Normally when trying to add some small feature in an old system too

        1. Yea, it’s definitely not optimal code, but most code isn’t.

  4. > We get it, somebody at Sun really liked OOP. The price for that obsession is that 20 years later, Java is still trying to reassemble itself into a language people actually want to use.

    🔥🔥🔥

  5. Seasoned developers gravitate toward elegant syntax:

    function atleastOneOdd(potentialOdds){
    return (potentialOdds !== undefined) ? potentialOdds.some((num) => num & 1) : false;
    }

    But your code is worse than your “bad” example. First, none of your code is commented. Next, nesting is not complex, it’s natural. Nesting is logical flow, and anyone who has taken the Structure of Programs with LISP will definitely prefer the nested version. If anything is an “anti-pattern” or “conditional inversion”, it’s your code, not the nested example.

    Next, the nested conditions are easier to read because they are logically indented, the logic flows like a circuit, the code is one line shorter, and the function uses both less memory and fewer clock cycles. It’s a win on all five counts.

    The only reason you think your example is “better” is because I am guessing you learned from professors who taught after the turn of the century, when CS instruction went straight down the tubes in most schools, as you elude to by saying they don’t teach efficiency.

    Your article’s title is clickbait.

    I can’t tell if you are a senior developer or a junior developer… or a student. Are you in school? Nobody in the professional world says “mutable” and “immutable”.
    No offense. Only Senior Developers (15+ years) should be writing articles targeted to senior developers.

    Your girlfriend is not a “beginner”. She’s student. Nobody calls a medical student a beginner doctor.

    Regarding isLetter(), why you are writing an article to “seniors” illustrating an introductory course homework problem? Or talking about your girlfriend?

    “In college, a computer science teacher will rarely give an assignment that has a performance requirement.”
    Is this true? Maybe after 2003, I guess. I wouldn’t doubt it, these days, but I don’t know. In the 90s, professors (not teachers) obsessed over Big O notation in every class. Then every college got on the bandwagon to make a buck offering a CS program, then the corporate outsourcing to India, then the Object Oriented nightmare, and well… if Big O is gone, that is sad. Given the bloat zeitgeist, you are most probably correct.

    “Everything has to be a class”

    Everything you say in this section is correct. Object Oriented Programming is utter nonsense. This whole generation is brainwashed, and they defend this fake religion with zealotry. Now it’s every programmers’ job to figure out this toxic emperor has no clothes. Dijkstra is right.

    Sorry to be tough, but if you want to publish as an authority, you have to be peer reviewed. You’re on the right track though!

    On the upside, you’re a very good looking man. Your girlfriend will be wise to hang on to you.

    1. I would argue that your proposed “more elegant” solution is not particularly elegant. “x : y ? false” is a rather convoluted way of writing “x && y”, wouldn’t you say?

      1. You are right!

        function atleastOneOdd(potentialOdds){
        return (potentialOdds !== undefined) && potentialOdds.some((num) => num & 1);
        }

        You’re solution is more elegant. Beautiful!

        I have spent too long coding in a language that, oddly enough, causes problems when interpreting this logic, and would at least require the ternary operator. So these logic bits have been off my radar screen for a while. Thanks for the reminder.

    2. I would just like to provide a counter argument that almost everything in this comment is wrong. Unfortunately, due to its excessive length and acute inaccuracy, I do not have time for a more detailed reply.

      If you want to “peer review”, you have to know what you’re talking about.

      1. “I would just like to provide a counter argument”
        Except that you didn’t provide a counter argument, so what you have written makes no sense to me.

        Stating I have no idea what I am talking about is not an argument. As a scientist, I always leave the door open to the possibility that everything I say and think is wrong, or more precisely, could be disproved, or proven otherwise.

        Maybe I don’t know what I am talking about. It’s possible. Nevertheless, regardless of the validity of my writing, what you have stated is not an argument, and has no bearing of the validity of my critique.

        Best to you.

        1. A scientist, wow, I didn’t know they allowed trolls to become scientists.

        2. @Big J,

          If you are writing here as a scientist, and expect scientist-y criticism, perhaps you should back your arguments with proper proofs or at least references.

          Such as:
          1. Why should your version of the code be accepted as more elegant.
          2. How have you determined that nobody in the professional world says “mutable” and “immutable”.
          3. Why is it that “only Senior Developers (15+ years) should be writing articles targeted to senior developers”.

          You refer to your unasked-for and uncalled-for comment as “peer review”, yet you yourself ignore the scientific method, as well as basic professionalism and ethics – but still make such demands of others.

          You may have some valuable points hidden in your comment, but they are ruined by the trash around them.

          Ultimately, despite possible mistakes in the article and possible truths in your comment, Ryland’s article makes the Internet a better place.
          Your comment, however, makes it worse.

          Disclaimer: the above comment is not a peer-review and does not aim to exhibit scientific publications quality or standards.

    3. Asteroids With Wings says:

      > Nobody in the professional world says “mutable” and “immutable”. No offense. Only Senior Developers (15+ years) should be writing articles targeted to senior developers.

      What on earth are you talking about? Literally everything you just said is wrong. I get the feeling that perhaps you are not part of this article’s target audience…

    4. @Big J –

      1. Your suggested code is neither elegant nor at all easier to read or comprehend than either of the syntax options proposed in the article.

      2. Your comments about lack of comment is both snarky, and plain wrong. Senior developers recognize that in the vast majority of cases, well-chosen names and clear, straightforward logic are preferable to comments.

      3. > Nobody in the professional world says “mutable” and “immutable”.
      I am in the professional world and I say “mutable” and “immutable”.

      4. > Only Senior Developers (15+ years) should be writing articles targeted to senior developers.
      Nowhere does the author say the article is targeted at senior developers.
      Regardless, this is just pretentious crap.

      5. > Regarding isLetter(), why you are writing an article to “seniors” illustrating an introductory course homework problem?
      I suppose that unlike you, the author thought it might be easier to get a point across using an example not taken from rocket science or quantum physics.

      6. > Or talking about your girlfriend?
      It’s called Personal Touch. Not every document needs to written in a research-paper style.

      7. > Sorry to be tough, but if you want to publish as an authority, you have to be peer reviewed.
      I’m sure the author thanks you. I thank you. Ney. Humanity thanks you.

      8. Do you know what “J” means in China? Let’s just say that it seems like you are indeed one.

      1. Humm…What the “J” means in China? (LOL)

    5. Chris Schaller says:

      I think you missed the whole point of this article, as such you potentially hail from the target subject of this article, and have validated it’s argument :).

      My wife is a primary school teacher, I used find it strange that she uses “shop talk” terms like “pedagogy” and “rubric” not just with her peers but with her 8 year old students. These are terms I didn’t hear once throughout my schooling, and yet they are used to discuss commonly occurring concepts within the class room.

      As a senior developer, if you are not using terms like “immutable” or “ecapsulation” to discuss concepts or during code review, then what convoluted language are you using instead? Try and find a single word that can be used to replace “immutable”… these are the vernacular of our trade, whether you learn it at school or on the job, you are demonstrating true understanding of programming when you can comfortably use these terms. Once you have mastered one language, these terms facilitate the learning of the others.

      I find this concept parallel to many English speaking persons trying to learn a new language for the first time, as has been noted, without clear rules for our own language, or without understanding what they may be, its a struggle to learn other languages.

      Are you actively participating in code review and are you able to convey the technicalities behind your concepts to less senior team members with confidence that they actually understand what you want them to achieve?

    6. I agree with the point that senior devs gravitate towards elegant syntax.
      All your other points are unnecessarily mean.

      > Nobody in the professional world says “mutable” and “immutable”.

      I do, and so do some of my colleagues.

      > Object Oriented Programming is utter nonsense.

      “Senior” developers understand that OO is a tool. As is functional programming, and structured programming. Et cetera. Sometimes one tool is better for the job than others. Senior developers don’t restrict themselves by digging trenches like this.

      And that last point about what the author looks like… “Senior developers do not comment on their peers’ appearances. In fact, any comment like this is absolutely unprofessional. If you were one of my colleagues, I’d consider reporting you to HR.

      1. But he is good looking! He’s very good looking, and looks are indeed very important. It’s compliment. That’s what you’re offended at? I don’t listen to disheveled people.

        “I’d consider reporting you to HR.”

        Then you are weak. There was nothing offensive in my entire comment. The fact that you are offended does not mean offense was given. That you don’t understand or like or agree with my points is not offensive. And I have never been offended in ANY situation when someone has complimented my looks. SJW’s are offended by absolutely everything; you have created a stale, boring, toxic workplace where you threaten everyone who disagrees with you. Your generation is lost.

        1. The comment on the author’s looks is not, in itself, offensive (although it’s for sure inappropriate).

          But overall I also think that your response was offensive and I am hardly a SJW.

          You say:
          > The fact that you are offended does not mean offense was given.

          If a person is offended then, by definition, an offense was given.
          If you mean that the offense was not *intentional* then yes, it’s possible, but maybe there is something for you to take from this as well.

          I am a non-SJW with more than the supposedly-prerequisite 15 years of professional experience and I’m telling you: you come off as offensive. Not “SJW Tree-Huggers Millenial offensive”, but just generally offensive. Your comment feels personal. Parts of it feel like you’re lashing out at the author himself rather than just focus on the merits of his work, and contain some remarks that come off as snarky.

          I’m still not sure if you are trolling, if you don’t care, or if you genuinely don’t see it. Just keep in mind that even if “SJWs are offended by absolutely everything” (as you wrote), it doesn’t mean that non-SJWs never get offended, and that every time any person gets offended it’s because he is an SJW, or weak, or born into the wrong generation.

          Maybe you should also practice what you preach and try to take something from the feedback you get here (from several people) rather than deflect it.

    7. Seriously, i totally agree on many aspects of your comment.

      The title is a total clickbait, if you just think for a 0.33ms what it means “to learn from others” then you understand that this post is about what YOU as a senior developer can learn, but then you read the post and see that it’s just a “top 10 mistakes beginners do”. Like. Hello? I was going to read about senior tips, not your girlfriend’s mistakes.

      Come on, man, you’re clearly not a senior. Even as a 2y junior i understand that.

      1. I think you might come to realize that as a senior a huge part of the job is to teach beginners. Assuming that’s the case, teaching others is literally part of your job and it should be honed as a skill like any other.

    8. OMG…thank GOD he didn’t mention his preferences for spaces or tabs!

      I could barely imagine your rancor if he did mention it, and you disagreed with his choice.

  6. Why does “isLetter()” only check lower-case? Developers should know “isalpha()”, and often the IDE will pop-up a description on hover. Even ignoring issues of locales, etc, “isalpha()/islower()” is immediately understandable, whereas reviewing that code involves checking A-Z are all included, the loop termination condition is correct, etc, etc.

    (Oh, and three “===” is not C++)

  7. There are some excellent points in the article, but I think it’s worth mentioning that some others seem to be very specific to countries/educations systems/schools/specializations of the people mentioned (especially the parts about not having performance requirements for code or teaching Java only).

  8. The direct isLetter solution gets the job only done in a programming exercise but not in real world. What about german umlaut characters (for example the ü in my name), what about characters with diacritics?
    Expanding the direct solution to cover real-world use cases is almost a never-ending story and having a magic array with hundreds of character constants if definitely not clean any more.

  9. Personally I find that teaching younger members of the team can be a two way street. Yes the experience we have gained over years of work can help them, but sometimes because they don’t have the filters on that we do when we look at code, they can see things that might appear radical to us but ultimately quite useful.

    I like the article though, and find myself in the same situations. An open and inquisitive mind is always going to soak up information from others, regardless of your level of experience.

  10. “check if an input char is within a specific ASCII a-z range” In before the EBCDIC fans come raging at your blog (and there are many of them!), consider using a look-up table instead. Example: https://stackoverflow.com/a/55951071/584518

  11. > While my girlfriend’s solution is definitely not the most efficient (memory or compute wise),

    It may seem obvious that her solution is inefficient, but no performance claim is *definite* until it has been tested. Far from being tangential or pedantic, this relates directly to the point about bias in the first paragraph of your post, and I believe it strongly supports your overall thesis.

    Modern C++ compilers are clever, and modern computer architecture is weird. When you see a loop that’s *obviously* unnecessary, your compiler (with optimizations turned on) is likely to notice this as well.

    https://godbolt.org/z/-jaF23

    As shown on Godbolt above, Clang compiled your girlfriend’s algorithm, and the algorithm you thought of, into exactly the same sequence of assembly language instructions. This is, of course, with the specific target architecture and optimizations I tested with. Different compilers, compiler versions, and targets will, in general, give different results.

    (This sort of optimization is not a recent development, though. Clang eliminates the loop–and array–from that code going back to clang 3.6, when -O2 or -O3 is passed.)

    GCC does not eliminate the loop. Even so, I *suspect*, though I have not benchmarked and therefore do not really know, that calls to her implementation will often be faster than calls to islower. (You mentioned isalpha, but based on the code you showed, what your girlfriend implemented and what you thought of implementing are locale-unaware versions of islower, not of isalpha.) I further suspect, but do not know, that this would be so even with less aggressive optimizations.

    Functions like islower and isalpha have to check the current locale, so they may not (and, in practice, I believe, rarely do) optimize into a simple add and compare, even in cases where they happen only to be used with the C locale, even in a program that contains no setlocale calls. Calls to functions in <cctype> and <ctype.h> are also not always inlined. For more on this general topic, see https://travisdowns.github.io/blog/2019/11/19/toupper.html.

    Considering that the algorithm you thought of is much simpler, and that the reason your girlfriend’s algorithm may be as fast is that Clang optimizes it into your algorithm (or, strictly speaking, optimizes it and your algorithm into something more efficient than either that is extremely similar to your algorithm), it may seem that the algorithm you thought of is preferable to hers.

    But this may not be the case, for reasons of correctness. Although most popular character sets are supersets of ASCII and therefore represent the lower-case English letters contiguously, there is in general no requirement that this be so (unlike with numerals 0 through 9). As written, a compiler that knows enough to be sure the algorithm you thought of is correct can optimize your girlfriend’s algorithm into it, but her algorithm also degrades gracefully when compiled for a target where that assumption cannot be made.

    I’m not advocating writing production code that compares a character against each letter of the alphabet individually or that appears to do so (and I very much agree with what you’ve said about how, with experience, programmers learn what functionality tends already to be implemented reusably). But the reasons to avoid it have little to do with performance. More generally, performance is an area where experience–at least general experience as a programmer–counts for less than one might think.

    1. Correction: What rendered as

      > Calls to functions in (or ) are also not always inlined

      was, of course, intended to say something like this instead:

      > Calls to functions in the cctype (or ctype.h) header are also not always inlined

      I had enclosed the names of those standard headers in angle brackets, which caused each to be replaced by the empty string, presumably due to being taken as attempts to form an HTML tag.

    2. “More generally, performance is an area where experience–at least general experience as a programmer–counts for less than one might think.”

      When databases are involved, I many queries are not optimal, nor optimal table cardinality, especially from cheap outsourced overseas developers. It’s easy to create a Cartesian join at the beginning of one’s software career, and that can have significant performance impact.

  12. You missed the most important one…

    “I am thou senior dev! My time is so important! I don’t have time to define a meaningful function name! Meaningful variable names??? hah!”

    function foo(bar) {
    if (bar !== undefined) {
    if (bar.length) {
    return bar.some((foobar) => foobar & 1);
    }
    }
    return false;
    }

    1. This really should have been in there. Clear variable naming is so important and so underrated.

      1. You also missed the most important another one…

        “I am thou senior dev too! A meaningful naming is so important! I must spant much time to define a meaningful function name! Meaningful variable names??? Too!”

        function thisIsAVeryImportentFunctionToInvokeInnerFunctionInClosureObject(myClosureObject) {
        if (myClosureObject !== undefined) {
        if (myClosureObject.length) {
        return myClosureObject.getMyClosureObjectMemberIsValidOrNot((member) => member & 1);
        }
        }
        return false;
        }

  13. The junior developer I’ve learned from the most as a senior developer, is myself

  14. I am basically a C developer since 2002. In this discussion, the majority was based on JS I would like to present the simplified implementation of the above mentioned example on JS(which I am not familiar with). If some variable is defined there must be some size, so the length checking might not required.

    var bar=undefined;
    foo(bar);
    bar = “Test”;
    foo(bar);
    function foo(bar){
    if(bar)
    return foo1(bar);
    else
    return false;
    }

    1. A variable could easily be an empty array.

      const empty = [];

      if (empty) {
      console.log(‘Am I really empty?’);
      }

  15. regarding “incept” i do not think that means what you think it means

    1. I’ll take the bait. What I think it means is

      https://dictionary.cambridge.org/us/dictionary/english/incept

      > to begin or introduce something

      I would love to know how I’m misusing it though.

  16. Abdur Rahman says:

    I guess there is an error in the code

    function atleastOneOdd(potentialOdds) {
    if (potentialOdds !== undefined) {
    if (potentialOdds.length) {
    return potentialOdds.some((num) => num & 1);
    }
    }
    return false;
    }

    It should be like this
    function atleastOneOdd(potentialOdds) {
    if (potentialOdds !== undefined) {
    if (potentialOdds.length) {
    return potentialOdds.some((num) => num & 1);
    }else{
    return false;
    }
    }
    return false;
    }

    1. But doesn’t that not change anything? What effect does the additional else have?

  17. The best thing I like in this post is actually the title itself.

  18. Prof.Pratik Bhattacharjee says:

    From my 18 years of teaching experience in computer science in both graduation and post graduation level, I am mostly in line with most of your points. The academic and industry are to different arena of programming. In academics, we emphasize on the understanding of the logic and get the program running, in the first place rather than optimizing the code ( also to write code as much as possible without using built-in function or code). This knowledge is automatically optimized when they do their final year projects. For example, the type of assignments given to them ( specially in papers like data structures) there is almost every function available to to handle the operations of stack/queue/linked list. So it will be meaningless if they use those functions like a black box without writing their own for the sake of optimization.
    It is not that the academicians do not know how things are going in the industry, but sometimes it is overkill or inappropriate to throw them to the beginners. They will soon loose the interest on the subject.

  19. My heaviest critique of the article is the title – ‘what senior developers can learn from beginners ‘ – isn’t conveyed in the body of text, which seems to be about what mistakes junior developers make.

    Additionally, I would defend the OO principle. Many languages adopt OO in some form; C has structs; C++ has classes; Python has it’s weird, quirky class objects; Java is entirely OO. Even more functional paradigm languages, like JavaScript, end up with pseudo-OO code.

    It’s a natural state of mind for a person to understand a thing as having self-contained properties. Object Car has Object Tyres. Object Tyres has Property AirPressure, TreadDepth, etc.

    Yes, there are times separate functions are good, and we shouldn’t seek to encapsulate everything into OO (a prime example are simple check functions like isalpha, etc). But OO is a way to containerise data in a way humans can understand it; functional paradigms are a way to process that data. You typically use both, neither is right or wrong.

    I think what senior developers can learn from junior developers are novel or interesting solutions to problems. Senior developers may have a grab-bag of fixed solutions (such as doing online searches), where-as junior developers might try novel or unexpected approaches. Most of the time they don’t work, but with any invention, sufficient number of failures leads to learning.

    I think the key aspect to keep in mind is that there can be multiple ways to solve the same problem, each with pros and cons. Efficiency, readability, security, stability, are all components that can conflict. Adding security adds complexity (what authentication mode do I use? How do I safely store this data? etc) that harms readability. Adding more checks improves stability but reduces efficiency.

    I tend to follow Python’s philosophy of ‘everyone is an adult’, and write the code to suit it’s context. Helper functions tend to be more efficient with few checks. High-level functions tend to have a lot of stability and security. You get the idea.

    Junior developers introduce creativity. Senior developers introduce experience.

  20. Carlos Nunes says:

    English is hard to learn!? Try German or Portuguese, then we can talk… 😀
    And sorry, consistence is not a strong point in Java.
    Just look at how many different namespaces do you have to deal with strings and date/time! (not to mention that they simply are not compatibles!)

  21. One of the most important “coding” skills that I learned was in college and has nothing to do with “coding skill”. It’s attitude. A lot of engineers will defend their code… “the mistake is not in my function, you must have passed it a bad parameter”
    Now I assume that the mistake is indeed in my code until I can prove it otherwise.

    While I am a “senior” engineer, I do not claim to be a “senior engineer” 🙂
    (30+ years coding/testing/training/mentoring/documenting/installing)

    I don’t write elegant code. I comment it because in 2 years when it comes back around, the most common thought is “what the heck was I thinking?” (okay, it’s not the word “heck” either)

    I don’t use the words mutable or immutable. It’s a variable or a constant. :p

  22. I’m sorry, but first your first example is just terrible. I know you were trying to demonstrate the if statement inversion (that’s what it’s called), but there are so many other wrong things with that piece of code, so there were other, more important opportunities for learning.

    First of all, it’s definitely JavaScript. I mean tell me of another language that doesn’t require type annotation, has === as equality operator and the function some on array.

    Now, JavaScript being a special kind of language, you’ll be forgiven for not knowing that testing for undefined is not enough. A variable can be null too, and because the strict equality operator is used, it means the second if statement will fail spectacularly with an error.

    But, regardless of the language, the first question to ask is, what does our function expect, and what do we need to guard against. Assuming we don’t want to throw any errors from the function, and that we want to return false, even when the object passed in isn’t an array, the refactor is much simpler. JavaScript has a method for that Array.isArray. The length test is useless. The window object has a length property, and yet it’s not an array, and furthermore, the method “some” works with empty arrays.

    Secondly, it’s important to understand what the method “some” returns. It returns a boolean, great! So we don’t actually need any if statements in this case, because we can use a logical expression! So instead the function should be:

    function atleastOneOdd(potentialOdds) {
    return Array.isArray(potentialOdds) && potentialOdds.some((num) => num & 1);
    }

    So here are two important lessons for you (that you can’t learn from the beginners):
    1. Requirements are important, understanding them is equally important
    2. Language particularities can lead to bugs if they’re not known or understood

    I don’t even want to comment on the rest of the article…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.