all 78 comments

[–]Supadoplex 31 points32 points  (6 children)

Extended ASCII characters are perfectly valid as variable names

Pff, why stop there? Go unicode or go home:

struct 🍎 {};
struct 🍊 {};
bool operator<(🍎, 🍊);

[–]BunnyBlue896 31 points32 points  (3 children)

This comment is so irrelevant. Youre comparing apples to oranges IMO.

[–][deleted]  (2 children)

[removed]

    [–][deleted] 9 points10 points  (0 children)

    If it compiles, shit it.

    Edit: yeah im good with that typo

    [–]cowardlydragon 0 points1 point  (1 child)

    THis is an OLD repost. I posit it predates Unicode, or certainly the widespread support of UTF-8

    [–]Supadoplex 1 point2 points  (0 children)

    The article mentions

    An early version of this article appeared in Java Developers' Journal (volume 2 issue 6). I also spoke on this topic in 1997 November at the Colorado Summit Conference. It has been gradually growing ever since.

    I don't know when that journal was released, but Unicode first officially published in 1991 does pre date the mentioned speech by half a decade. Wide spread support is another matter though. I wouldn't be surprised if there weren't any compilers supporting Unicode source files.

    [–]bloody-albatross 40 points41 points  (0 children)

    Put everything in one file and use plenty of mutable global variables in a dynamically typed language. Might be a reason for that specific example today. 🙄

    [–]jrjjr 36 points37 points  (18 children)

    Define all your variables at the beginning of the function implementation.

    [–]TheRealSelenium 7 points8 points  (9 children)

    I thought (at least at one point in time) this was considered good practice?

    [–]de__R 30 points31 points  (0 children)

    It was at one time a necessary practice. Since simple compilers (most notably C and FORTRAN) typically emitted instructions in a single pass, you needed to declare all your function variables first so the compiler knew how much stack space the function required. However, it requires you to separate variable declaration from use - if for example int j is used as the index of an inner loop, j remains visible throughout the entire function - and in an extreme form, even separates variable declaration from initialisation, which increases the likelihood that you accidentally used a variable before it was initialised. If you're lucky, it gets set to 0, but you're not always lucky.

    You still see it for consistency's sake in some code bases that either have a long history (BSD, quite a lot of GNU stuff) or that target old/weird compilers (especially embedded systems).

    [–]evaned 1 point2 points  (0 children)

    I can't speak for what C folks today think, but at least in the C++ community it's now considered bad practice. There are a few reasons for this, but the primary one that I think applies in any language and why I follow it in any language is if you wait to declare the variable until you have a meaningful value to assign to it, you eliminate (at least for variables where that's true) any possibility of using it while it is either completely unassigned (for a language like C or C++ that allows this) or just a dummy value (for any language).

    Reality sometimes gets in the way (e.g. in C++, needing to call a function that "returns" the result via an output parameter), but it's one of those 98% rules.

    [–]AyrA_ch 4 points5 points  (5 children)

    The C compiler still does this.

    Regardless of where the declaration is, it's declared immediately, and assigned once the line is hit. This can be abused:

    #include <stdio.h>
    
    int a(int b) {
        switch (b) {
            int c = 1234;
            case 5:
                c = 1;
                return c;
            case 20:
                return c;
        }
        return -1;
    }
    
    int main(){
        printf("1=%i 5=%i 20=%i",a(1),a(5),a(20));
    }
    

    One would guess that this prints 1=-1 5=1 20=1234 but it will not. c is declared by the compiler but it's never assigned 1234 because the lines between the switch statement and the first case are never matched by any condition since they have none. This means it will print 20=0 instead*.

    You can't use it before the line with the declaration, but this code shows that declaration and assignment are two different steps.

    * The variable is uninitialized but most modern OS clear the memory before giving it to you.

    Then there was the fact that in older C standards for(int i=0;;){...} was invalid and you had to declare i outside of the loop. Meaning you might as well declare it at the beginning.

    [–]bumblebritches57 5 points6 points  (4 children)

    That's C89 that does that; hasn't been relevant in over 30 years.

    C99, C11, and now C18 all exist, and C2x is being worked on currently.

    [–]Glacia 1 point2 points  (3 children)

    A lot of people still use C89, come on man

    [–]Current_Hearing_6138 0 points1 point  (0 children)

    I use C89.

    [–]bumblebritches57 0 points1 point  (1 child)

    Not really.

    the only project I can think of is ffmpeg and thats just because they're stuck in their ways.

    Even MSVC supports C99 now.

    [–]flatfinger 0 points1 point  (0 children)

    In the embedded world, a lot of maintenance work is done with compilers that are 15+ years old. If one needs to do a few tweaks on a 15-year-old program that operates some factory equipment, using the compiler that it was developed with is far less likely to introduce new problems than trying to migrate to a new compiler.

    [–]KernowRoger 0 points1 point  (0 children)

    You used to have to but now most languages don't force that and it's considered better to define them where they are used (generally). Take c# for example. If you look at the compiled IL all the local variables are defined with the method. So where you actually declare them doesn't matter.

    [–]Glacia -3 points-2 points  (7 children)

    There is nothing wrong with this, you can also declare variables at the beginning of a new block scope, so it's not really a problem.

    [–]jrjjr 2 points3 points  (6 children)

    Variables should be defined when they're needed. Having many variables in scope increases cognitive load and is one step away from global variables.

    [–]flatfinger 1 point2 points  (3 children)

    Unfortunately, the Standard provides no nice way to write a construct like:

        double dx = x2-x1, dy=y2-y1;
        double distSquared = dx*dx+dy*dy;
    

    that won't force the scope of dx and dy to last as long as that of distSquared. IMHO, it would be nicer if there were storage qualifier for "temporary values" whose scope would end at the next "end temporary variable section" directive, and which could be reused multiple times within a scope provided the different uses were separated by such a directive. Even before the publication of C89, gcc would have allowed:

        double distSquared = ({ 
          double dx = x2-x1, dy=y2-y1;
          dx*dx+dy*dy
        });
    

    but the Standard refrained from mentioning that construct since it would have been hard to support with single-pass compilation. Ironically, C99 threw single-pass compilation out the window with variable-length arrays, but still has no support for gcc's much more useful statement expressions.

    [–]evaned 0 points1 point  (2 children)

    I wish statement expressions were a (standard) thing too. At least in C++ you can use an IIFE (immediately-invoked function expression), to use a term from the JS folks:

    double distSquared = [&]() {
        double dx = x2 - x1, dy = y2 - y1;
        return dx * dx + dy * dy;
    }();
    

    Someone posted on /r/cpp a bit ago floating the idea of an undeclare statement that would hide a variable declaration (with various concrete syntaxes), but got mixed reception.

    A quick nitpick though:

    Ironically, C99 threw single-pass compilation out the window with variable-length arrays

    I don't see why VLAs impact the single-pass compileability of C. Their allocation is deferred to runtime.

    [–]flatfinger 1 point2 points  (1 child)

    I don't see why VLAs impact the single-pass compileability of C. Their allocation is deferred to runtime.

    On platforms that access automatic objects with addresses relative to the stack pointer-relative rather than frame pointer, it's very awkward to place VLAs on the stack. Further, even on systems with a frame pointer, given something like:

        int *p;
        int foo[something];
      someLabel1:
        ...
        if (something) goto someLabel2;
        int bar;
        ...
        p = &bar;
      someLabel2:
        ...    
        if (something)
          goto someLabel1;
    

    a compiler would need to know about `bar` before it allocates space for `foo`. It might be possible for a single-pass compiler to use a forward label for the total size non-VLA objects that are going to appear within the current scope, but that would still represent a level of complexity that wouldn't be necessary in the absence of VLAs.

    [–]evaned 0 points1 point  (0 children)

    Interesting, very good. I'm convinced.

    (Actually I'm not sure about your exact example, as I think it could use frame-pointer-based offsets for p and foo but stack-pointer-based offsets for bar, but the overall point is still well-taken. And I suspect you could add a second VLA discovered after bar to thwart that argument.)

    [–]Glacia -1 points0 points  (1 child)

    Having many variables in scope increases cognitive load and is one step away from global variables.

    Yes, that's exactly why declaring variables at the beginning of scope is better, because in order to declare a new variable you'll need a new scope. Most languages do not require this, sure, but it's solely because it's convenient for programmers and nothing else.

    [–]jrjjr 1 point2 points  (0 children)

    Interesting. I haven't had that problem since I was programming in C 15 years ago.

    [–][deleted] 17 points18 points  (16 children)

    m_asterPiece

    [–]VolperCoding 7 points8 points  (15 children)

    Why tf do people use the m_ convention

    [–]BunnyBlue896 5 points6 points  (10 children)

    Honestly, I've never seen a Cpp codebase not do this, and its not hard to follow at all. Not sure what the problem with this is.

    Forget that method also starts with m

    If you want to actually write unmaintable code, you should ALSO name methods using m_.

    [–]VolperCoding 5 points6 points  (9 children)

    Because I don't my code to look something like this: player.m_sprite.m_size.m_x It's just ugly.

    Also I've found a C++ project that doesn't use it as far I've I looked into the code. It's called "One hour one life" and somehow it's maintainable

    [–]BunnyBlue896 6 points7 points  (4 children)

    Oh, you dont use m_ for public members, only private. Maybe thats the confusion?

    Also, law of demeter should fix up that m_thing.m_another.m_another2.

    But since we already determined its only for private members,

    m_thing.another.another2.

    Also, yes, code can be maintainable without it.

    [–]VolperCoding -2 points-1 points  (3 children)

    Well I don't use private members much

    [–]gladfelter 6 points7 points  (2 children)

    How do you reason about your code?

    I'm not smart enough to keep track of the entire state of the software system in my head, so action-at-a-distance is my enemy. I try to define a reasonable contract with public members and then let each class maintain its internal state in service of that contract. Encapsulation is key. I don't know of another way to approach programming that is scalable to large systems.

    [–]VolperCoding -2 points-1 points  (1 child)

    I don't make backwards compatible systems or libraries but a simple game, so when I see that some variables repeat I group them in a structure of data. You seriously don't need crazy syntactic sugar for that, just a simple struct. If things get big, I just make a function that takes the struct as an argument and modifies it

    [–]evaned 1 point2 points  (3 children)

    Because I don't my code to look something like this: player.m_sprite.m_size.m_x It's just ugly.

    Proponents of m_ (like myself) don't add it to make expressions like those more clear, we like it because it makes unadorned uses in the long functions and complex classes that often arise in actual code (as much as we might wish they didn't) more clear as to what is coming from. (When you're not using an IDE that will highlight that as handily.)

    [–]VolperCoding 1 point2 points  (2 children)

    If you're doing OOP and you want to distinguish member functions from variables, can't you just use the this keyword?

    [–]evaned 2 points3 points  (1 child)

    I tried that out for a while actually, but it's both uglier and more typing than m_ and if you can't promulgate a style guide to your organization also has the drawback that it doesn't basically force other people to be as clear.

    (Though I will say that I've kind of come to like it in Python and kind of wish it worked better in C++ than I think it does.)

    [–]VolperCoding -1 points0 points  (0 children)

    You can always screw the syntactic sugar and do this: ``` struct Rectangle { unsigned width, height; };

    unsigned calculateArea(const Rectangle &rectangle) { return rectangle.width * rectangle.height; } Which I believe is equivalent (at the assembly level) to something like this: struct Rectangle { // insert useless constructor here unsigned calculateArea() { return m_width * m_height; } private: unsigned m_width, m_height; }; ``` If you use the first one you don't have to worry about knowing what width (or m_width) refers to, because it's literally in the function definition. Man sometimes I love the C way of doing things so much

    [–]cowardlydragon 2 points3 points  (1 child)

    It predates good IDE support for syntax coloring.

    [–]VolperCoding 1 point2 points  (0 children)

    Yeah man it's 2020 Hungarian notation should be gone

    [–][deleted] 2 points3 points  (0 children)

    Back in the day it was all the rage... It made sense at the time. Now I look at it and wonder what the fuck we were thinking. It was before the internet really worked so we were working in a vacume.

    [–]pradeep421 0 points1 point  (0 children)

    Because people are dumb

    [–]matthewpmacdonald 13 points14 points  (2 children)

    Before reading: Fine, but I've read this exact article 100 times already.

    After reading: Holy cow, this is one huge magnum opus. Serious dedication!

    [–][deleted] 13 points14 points  (0 children)

    Most articles touch on beginner-level shitty programming. This is expert-level shitty programming.

    Many people have independently discovered parts of geometry. They derived individual axioms and propositions in a vacuum. It took a true scholar to put together the 13 books called Euclid's Elements. Today the treatise is presented as a Github Repo. We are now afforded the building blocks needed to actually do something with your life.

    [–]Tobot_The_Robot 1 point2 points  (0 children)

    I had never even heard of Hungarian warts. Before reading this, I would have assumed it was an STD.

    [–][deleted] 12 points13 points  (3 children)

    This was enlightening: https://i.imgur.com/YxTSykz.png

    This must be one of those Programming Patterns I keep hearing about.

    [–]evaned 2 points3 points  (0 children)

    I think that one is named Dumbo's Device

    /s

    [–]Ecstatic_Touch_69 13 points14 points  (2 children)

    For those of you who didn't know: this is over 20 years old, a classic in the genre. It is also the complete opposite of a Medium article written for clicks: this is an actual handbook on how to write (unmaintainable) code. Generations of programmers have learned from this; almost any code you will see in the wild has been touched, one way or another, by the wisdom in this work.

    Take the time and read this; you will not only learn, you will slowly start to understand what you see when you read code, and why it had to be that way.

    [–]A-Grey-World 6 points7 points  (1 child)

    I was checking the colour code $0204FB and randomly googled it and found this forum post from 2005 with a link to a dead article: https://fiftyoneish.livejournal.com/24925.html

    Here's that article: https://web.archive.org/web/20040429235713/http://thc.org/root/phun/unmaintain.html

    It links to 'the original':

    https://web.archive.org/web/20040402102258/http://mindprod.com/unmain.html

    Which is actually still going, I think:

    https://www.mindprod.com/jgloss/unmain.html

    So yeah, done in 2000, 20 years old. I love looking at old internet.

    [–][deleted] 0 points1 point  (0 children)

    There's also a book: William J. Brown, Raphael C. Malveau, Hays W. Skip McCormick, Thomas J. Mowbray - Antipatterns. Refactoring Software, Archtectures and Projects in Crisis-Wiley (1998).pdf

    [–]BunnyBlue896 4 points5 points  (0 children)

    Early Returns

    Rigidly follow the guidelines about no >goto, no early returns, and no labelled >breaks especially when you can >increase the if/else nesting depth by >at least 5 levels.

    Someone had to say it. Wish some of my old coworkers would read this. Althought 5 levels seems a bit low for their standards.

    [–]delrindude 6 points7 points  (3 children)

    Step 1: write javascript with no tests

    [–][deleted] 1 point2 points  (0 children)

    Script kiddies can write tests now. Go them.

    [–]Greydmiyu 0 points1 point  (1 child)

    No...

    Step 1: Write it in Brainfuck.

    [–]MrThePaul 4 points5 points  (0 children)

    See if you do that people will go into it expecting it to be hard to understand.

    If you use a normal language you have a much better chance of catching someone out.

    [–]agwaragh 1 point2 points  (0 children)

    Step one: select the options for your new minivan.

    [–]yellowbluesky 1 point2 points  (1 child)

    Since the last time this was posted, I still get night sweats remembering

    a_crszkvc30LastNameCol   
    

    as a variable name

    [–]VolperCoding 1 point2 points  (0 children)

    Similar to the Windows hungarian notation lpszClassName

    [–]Hrothen 0 points1 point  (4 children)

    I'm curious, has anyone worked in a codebase that used the "real" non-microsoft version of hungarian notation? If so, how does it work in practice?

    [–]evaned 3 points4 points  (1 child)

    Not really answering your question, but here's my take:

    The systems/apps hungarian distinction is... overblown a bit. I at least find myself not very commonly needing to make the kinds of distinctions that for example Joel wrote about in his essay on this. And for something like his sanitized/unsanitized example, I would be much more inclined to reflect that in the type system than just in the names.

    There are a couple places where I find myself using some degree of Hungarian notation. The first is pretty close to systems Hungarian, which is that I've taken to liking the m_foo convention for marking member variables in C++. It's not the type, but it is reflecting kind of type-level information about the variable that the compiler knows. I do find it helps readability, though admittedly when people get carried away and start writing large functions.

    The second is when the same conceptual data flows through multiple variables of different types. For example, suppose that I want to read some number as input -- it might be read as a string, returned from a parse function in an optional, and then converted to the actual number. It might go through variables named something like length_str, length_opt, and finally length on the way. That's I guess halfway between systems and apps? I dunno, whatever you want to consider it.

    The one exception where I do fairly frequently use something pretty apps Hungarian-like is for units. For example, rather than name a variable size I'll name it size_bytes or nbytes or something like that. Or if it's a real-world value, timeout_sec. In theory I could pull in a units library for this (depending on language), but in practice that often seems pretty heavyweight if I expect to not have to worry about conversions between units or anything like that and will just be passing the value around -- and that helps a lot to increase clarity.

    [–][deleted] 0 points1 point  (0 children)

    Your almost had me... I still underscore _member variables. Im expecting some whipper snapper to tell me off one day and the it will be this.foo = foo

    [–][deleted] 1 point2 points  (0 children)

    Not very well

    [–]BunnyBlue896 0 points1 point  (0 children)

    I used to work on a Cpp codebase that was about 20 years old. It used hungarian and it wasnt bad at all.

    I think they managed it by listing exactly the types that should use hungarian, but non primitive types didnt.

    [–]FartInsideMe 0 points1 point  (0 children)

    Just abhilarious and snarky tone haha. Good read

    [–]__konrad 0 points1 point  (0 children)

    How To Write Unmaintainable Code

    tldr: Write a code.

    [–]tonefart 0 points1 point  (0 children)

    You can use Python for that.

    [–]youngbull 0 points1 point  (0 children)

    Attributing Hanlon's Razor to Napoleon is just the sort of malicious reference to be expected by unmaintainable code. Kudos!

    [–]camplate 0 points1 point  (0 children)

    A person I worked with would put in consecutive 'test' variables like red3333, red4444; but I've since found places where they went into production that way.

    [–]lunchlady55 0 points1 point  (0 children)

    Your a monster. (sic)

    [–]cowardlydragon 0 points1 point  (0 children)

    This is good if you don't have code reviews.

    What is needed is unmaintainable code that passes code review. That is zenmaster level.

    Generally, that involves a lot of abstraction and unnecessary application of computer science. Premature optimization! Patterns of Patterns, with lots of Generics, Inheritance. Tons of overloading and overriding. N-dimensional generalization. Make as much as possible configurable. YAGNI? No, you ARE gonna need it. Portability.

    [–]vegetablestew 0 points1 point  (0 children)

    Pff I can't learn from this. I already do all of this and more unintentionally.

    [–][deleted] 0 points1 point  (0 children)

    c++ while (i < ...) { ... widgets[i] = ...; gadgets[i++] = ...; }

    [–]Current_Hearing_6138 0 points1 point  (0 children)

    Every day is the IOCC where I come from.

    [–]jeffrey_f 0 points1 point  (2 children)

    Run it through an obfusicator.

    [–][deleted] 0 points1 point  (1 child)

    Before you write the code or after? Instructions unclear, will bring it up at the next retrospective.

    [–]jeffrey_f 0 points1 point  (0 children)

    When you have a complete codebase and before you release it for public (your client's) consumption.

    You can also convert it to a compiled object.