Single letter variables do tell you something about the variable. They say "I'm not important", "my scope is as small as I am", "I'm fleeting" and "I'll be gone in 2 lines".
Yeah exactly. The more imposing the variable name is the bigger the scope. Since letter variables are for a few lines of code in the middle of a function. Large screaming snake case constants are constants that span 1 or more files.
Still doesn't justify using i instead of index, or using i, j instead of rowIndex, colIndex. or k,v instead of key, value, or...... you catch my drift.
@@Daren6111 as the original comment says. Just for very simple variables which have no meaning other than iterative variables like “int i”, or “char c” for one line variables. Or, like lambda function parameters (k, v) for map’s. Though I do see value in naming row / col.
off by one errors are like trying to plug a usb into its slot. You always bugger it up first try despite doing the same thing forever. Sometimes a third attempt is needed somehow 🤣
I ran into this today, got a piece of old Amiga C code I wrote as a teenager 30 years ago and couldn't for the life of me figure out what it meant. Really glad I've stopped doing that later in my programming life lol! And there were many other bad habbits like experimental code that's left in the code base without comments or documentation, it's no longer used. I kept it as a repository so that I could refer later to it. There was no version control back then. And GUI, util, handlers all mashed together AND spread apart in multiple files. It's a total mess! No wonder that the code was in a half baked state. I've managed to get the base functionality working again, thanks to the debugger, which I didn't understand how to use back then. Another mistake I made back then was naming functions inconsistently like CloseProjectWindow vs ProjectWindowClose and confusing the two. And then using PICloseWindow, using an abbreviation I don't remember and ordering it in yet another way.
I bumped into this when trying to modify an open source game, but I couldn't figure out what anything did because all the variables were incredibly vague abbreviations.
Even with context they can be hard to read, and without context they are impossible to read I do think he should have explained why code (in particular old code) used abbreviations. Maybe because it required less typing in a world without intellisense? If you consider the function DefWindowProc (in the Win32 API from 1995), does "Def" mean "Default" or "Define"? Does "Proc" mean "Process" or "Procedure"? The function header is: LRESULT DefWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) What is an "LRESULT"? What is an "HWND"? What is a "UINT"? What is a "WPARAM"? What is an "LPARAM"? "HWND" is a WindowHandle. A handle is the ID of a resource managed by Windows. Not a pointer (so nothing to do with physical addresses), just an ID that identifies it (like a social security number identifies a person). "WindowsId" is more descriptive than "handle", but the word "Windows" is overloaded because it could be talking about the operating system or an actual window. So "WinId" might be better than "WindowsId". So maybe "HWND" could be "windowWinId" instead? "UINT" is an unsigned 32-bit integer. Why not call it unsigned32bitInteger? If the 32 needs to be flexible (maybe 64 or 128 some day) then it could be unsignedNativeInteger? Integers are common enough that "uint32" would probably be an okay abbreviation (the fact that the value 1 is physically stored as 15 zeros, then a 1, then 16 zeros is a whole other can of worms but I'll ignore that for now). And how is a "Msg" (maybe "message" would be better for the name for the parameter) which is usually something human readable that is meant to be read by humans just an integer in this case? Is it an instruction to the window? Maybe "windowInstruction" would be better than "Msg"? Even in a world without intellisense, a lot of abbreviations make no sense because it takes more effort to remember the abbreviations than it does to type out non-abbreviated versions. But... BUT... abbreviated code fits nicely in a 320x240 screen. So, we should all use abbreviations like "WPARAM wParam, LPARAM lParam". 640x480 killed the need for abbreviations. But it was just "the way things were done" so the practice went on for a long time after 640x480 was mainstream. Now a days we even have 800x600 so... yeah...
I personally wouldn’t go that far. While abbreviations like URL and DAO do require some context, I’d rather write ImageUrl or somethingDAO rather than imageUniformResourceLocator or somethingDataAccessObject. Somehow these names are less comprehensive than their abbreviations.
Retired engineer here - started with Fortran with punched card decks and of course assembly code. I remember when "real engineers could do everything in 8k of core". Every byte was precious. It was beat into our heads to write tight code. As a young engineer in the late seventies I recall struggling with code maintenance and reuse. I shared my frustrations with another engineer who asked a key question that changed everything for me. He said something like "when was the last time you had problems fitting a program into memory?" I couldn't remember. Our newest designs had faster CPUs and more memory. After that I programmed for readability and reuse. Execution time critical or "real time" code was kept separate and avoided when possible. Thanks for sharing these tips with others.
Instead of asking about fitting programs into memory, we should asking about AAA games from multibillion dollars companies having stutters, fps drops, fitting on SSD, fitting into memory with Discord, MSI Afterburner, OBS and browser running... And the answers will be today, all day, everyday.
I was just a bit later than you in the technology curve. But tight code was still important and it's a hard habit to break. Even the variable name would take up space and we'd think about what branches of the code would be called most and put those first. I just started programming python in an IDE and, wow, what a difference. The video's thoughts about long variable names is spot on. The IDE suggests/fills in the name and if you do get a character wrong it highlights that the variable isn't being used elsewhere, no need to wait for compile or runtime errors. And to think that back in the day we avoided interpreted languages due to their slower execution times. Kid's don't know how good they have it!
The "if you're having trouble naming something, it probably means something needs to be restructured" rule of thumb is honestly my biggest takeaway from this.
@@josk8936 When you look at your code the next morning and start finding variables named "fuck_it" and "iGiveUp", that's your cue to maybe sleep more lol #soivebeentold
The actual version of the starting quote should be: "There are only two hard things in computer science: Cache invalidation, naming things, and off by one errors"
Gets to the point immediately, concise and clean presentation, not too long, speaks clearly, ends when the subject is complete. This is a recipe for a lot of subs
Back in the 60’s variable names were limited to seven character. Additionally, by default variables starting with the letters I, j, k, l, m, and n were by default integers. We often used i, j, … as loop counters. Hungarian notation was named after Charles Simonyi, a programmer who worked at Xerox PARC circa 1972-1981, and who later became Chief Architect at Microsoft.
@@erato1look into "apps hungarian" vs "systems hungarian" tldr: hungarian notation in its original form is good, but the windows API guys corrupted it
@@erato1 I still use it in C/C++. "pcwszLastName". Just by looking at it away from the declaration you now know it's a pointer to a const UTF-16 character string that's zero terminated. And it's buried 5 levels of nesting deep in a 200 line linear function with a single return at the bottom. With a comment block on top. Horrors!
It should also be noted that when Simonyi referred to 'types' he was *not* referring to variable types... he organized the code by the 'type' of thing that it was doing... drawing a window on the screen, capturing mouse movements, etc... the variables might be windowMenuBarHeight, windowMenuBarWidth, or mouseXCoordinate, mouseYCoordinate... the 'type' was essentially a tag that kept things organized semantically. Simonyi worked in the Microsoft applications division... when Hungarian Notation was adopted by the operating system group, the rule that 'variables should start with their type' got communicated, but the meaning of 'type' was lost.
One other rule I'd add: If you have two variables that contain similar or related data, give them names that tells me what's different about them. One maddening example I ran into was an order processing program that calculated shipping cost. And it had three variables named "frght", "freight", and "freightcost". I eventually figured out that one of them was the freight cost currently on an order that was being updated, another was a standard shipping cost taken from a table, and the third was a calculated shipping cost using a formula that only applied in some special situations. But wow, figuring that out took a lot of effort. If the original author had just called them, say, "currentFreightCost", "standardFreightCost", and "specialFreightCost" or some such, it would have saved me a lot of time.
I was just scrolling down to write the same thing! This is a huge trap that's a lot more common, and a lot harder to deal with, than anything mentioned in the video imo. So often in commercial codebases I'll see functions with variables like "fileList," "fileArr," and "fileTable," -- why do we need three copies of the same thing? What's different about them? Why is this one updated here, but not the other two? It boggles the mind and leads to a lot of wasted energy, especially when the original developer is long gone.
One thing that helps is also commenting your code ._. I don't understand why people are simply ignoring the easiest way of solving all those misunderstandments(even though, of course, naming variables good is the best and most efficient way).
I think the one exception to the "no single character variable names" rule is the naming of for-loop iterators. It's extremely common for such iterators to be named "i" and "j". If a developer looks at some code with an "int i;", they immediately know it's such an iterator.
I'll break this rule in Python if I'm iterating an iterator other than "range". "for item in cart" works well, but "for iteration in range(5)" really doesn't especially once you start nesting loops.
I used to agree with this BUT I almost always avoid for() loops in modern code. Usually when I am iterating across a collection, I want the iterated elements but not the actual index. And when I do want the index it is actually nice to have it called "index". Of course if you are still coding in C99 or earlier, then the pattern of using "int i;" makes a lot more sense
Another one i can think of is if you are expressing coordinates/position, then you could have like an x and y variable denoting the position along the x and y axes
i & j is a bad combination as they are visually easy to mix up, especially for people with dyslexia. You also find another version of the problem with i & y or c & z. Both these pairs are a pain if you have to verbally talk about your code.
@@JoeSteele No you're right, there shouldn't be exceptions to the rule. If you need the index your standard library has a function for it. Pseudo code: for item in collection { ... int index = collection.indexOf(item) print(index) // There ya go bud } Another major issue of one letter iterators is that sometimes you have multiple loops with references to different iterators at different scopes. Things like working with multi-dimmenssionnal arrays for example. You will need to write multiple loop with i, j, etc... and use those iterators at different "level" of iterations which just make the entire code confusing.
My absolute favorite example of naming variables in a program: The original programmer named all the integer variables i1, i2, i3, etc and all string variables s1, s2, s3, etc. And then apparently to "save memory" or something, he would re-use variables in different parts of the program for different things. After banging my head against the wall for hours trying to figure out how to make the changes I had to make, I decided to take a step back. I studied the program and figured out what each variable actually was, and did a search-and-replace to rename it to something sensible.
Reusing variables might just come from ASM times Back then there weren't really things like names You just had some adress in memory you wrote onto And when the data at the adress fullfilled its purpose there'd be no reason to keep it occupied with no longer used data And with how important micro-optimsation could appear there was a good reason to do so But as soon as things hit anything that scoped variables there shouldn't possibly be any upside to reusing variables
That's the only sensible way to deal with such code. Rather put in some more work once than banging your head against a wall each and every time you have to get back to it.
Another nameing convention I've adopted is this: if I have a class that belongs to some "feature", like a "knife" belonging to a "watermelon" feature, I prepend the feature to all the classes that support that feature: "WatermelonKnife" - next, if this class has a reference to another class within the same feature, for example, "WatermelonKnifeSharpener", I will omit as many of the shared prefixes in the reference property name - to just "sharpener" in this highly contrived example. Then, my code might read something like "MyWatermelon.Knife.Sharpener...." instead of "MyWatermelon.WatermelonKnife.WatermelonKnifeSharpener..."
This is a good example! I find GoLang extremely useful for this since it is a package oriented language. And you are obligated to specify the name of the package as a prefix, except if you are in your own package. E.g. Let’s say I have a domain ‘vehicle’ logic and I want to have a factory that makes vehicles. In another package, the type would be represented as ‘vehicle.Factory’ and you would make it as ‘vehicle.NewFactory’ instead of ‘vehicle.NewVehicleFactory’. I find this extremely useful since it’s built into the language and I don’t have to battle with my colleauges about good names. Also, another tip for naming files: If you have a ‘vehicle’ that’s an interface, that interface would go in ‘vehicle.go’ file. The implementations would each have their own separate file called ‘vehicle_car.go’, ‘vehicle_truck.go’, ‘vehicle_cached.go’. This way, the interface and all the implementations are grouped together in a directory structure and not scattered across the directory, plus they are easy to search and access.
. But I can instantiate a Watermelon object - if I have a Watermelon namespace, I end up with something goofy like Watermelons.Watermelon. I use namespaces and schemas in SQL for the brand of the whole application, or plugin.
Like FruitCity would be the name of my business, my namespace, and application. I would never instantiate FruitCity, that would be like my business's name. I might use objects from a System namespace, but I don't manage those, Microsoft does. And maybe I use a CuttingBoard plugin, and maybe it has a KnifeSharpener that is different than mine, but I can still use the plugin, because of namespaces.
I was on the no-single letter names train for a long time. I've since come around. Single letter variables can be incredibly useful as aliases when you need to repeatedly refer to the same item multiple times in the same line. The trick is the single letter name should be very short lived. It needs surrounding context so someone can infer what it is. Consider the Python code `new_columns = ['prefix.' + c for c in columns]` versus `new_columns = ['prefix.' + column for column in columns]`. I think the first one is much easier to read.
Thats true, i think it is an acceptable exception. I do this sometimes in very small scoped / short lived arrow functions or lambda expressions as well, like persons.filter(p => p.getAge() < 16), if the surrounding context is immediate or its a long chain of one liner stream operations. In such cases you dont need very long or descriptive names really as they may just lead to more breakds and prolonging the lines of code in the method/function you want to keep small (but readable). But in arrow functions which are a bit longer or its less clear i still use full names as i do in pretty much any other case. I was trained in my first professional environment during an apprenticeship by a real "oldschool" hardcore senior Java dev for 2 years, who was a very kind and brilliant man - i read many important books because of him and learned from him that no name is too long and i adopted this ever since (of course, there is such a thing as too long, but you get the gist). I only went to university after i spend years in the industry, and my fellow (younger) students were sometimes really confused why my code is so "long" (not vertically, but horizontally). Or why i split code in so many functions/methods/classes. Funnily enough, to them it made to code "less readable/understandable". But that is usually born from a lack of practice in both writing and reading code at that point still. They are not "used" to how code looks and reads other than throwing some stuff at the wall until something sticks and the compiler is mute enough (with suppressed warnings) to fulfill some early grade assignments. Not used to scopes larger than 1-2 files. I guess theres still a core of truth to staying "as brief as possible", which is subjective.
@@stylishskater92 There is the famous saying - "any fool can write a program a computer can understand, but only good programmers write code a human can understand" 😉 For the splitting of functions/methods/classes, I believe a sensible middle ground has to be found. Often, splitting is really useful for self-documenting code. On the other hand, I saw people replace "if (end < begin) { invertFunction() }" by "if (shouldInvertFunction(begin, end)) { invertFunction() }". The first one is clearly more concise AND it tells you right away what the condition actually is. Here extracting makes the code harder to understand. Also, it obviously does not make sense to build a nice API with fluent builders, if people then extract the construction of an object to a method like "createObject(id, name, foo, bar, anotherProperty)"... As for single letter vars, I fully agree. OK for lambdas, list comprehensions etc, maybe as a loop counter (if anyone still runs classic for loops...?) but otherwise, something to avoid.
@@nvcbl Very true. Extracting a variable CAN make sense in this case to avoid re-evaluating every iteration, but please call it after what it contains, not after what it's for ;-)
The C# interface naming convention exists because C# code (including the .NET libraries) makes extensive use of interfaces, and often has classes that implement those interfaces but have the same name as the interface they implement. There's an argument to be made that this itself is an antipattern, but when you're dealing with just as many interfaces as you are classes, it really helps to be able to differentiate them at a glance, and to know what types you can and can't instantiate. Tangential, but when I was first learning to code I didn't really get the interface pattern (or many OOP principles) and I very quickly learned that you couldn't use the "new" keyword on any type with a name that began with an "I" - this encouraged me to figure out what exactly an interface even was.
This. It is also the reason why if I write code for myself I actually use `A` for abstract classes. It's always annoying to try to instantiate those then find out "oh it's abstract, so I have to search which types even implement that" In my opinion it's even good for readability `AStream` or `AWriter` or `ATruck` makes immediate sense.
there’s value in following the conventions of the eco system you work in. There are rules for dotnet APIs, pascal case for externally visible identifiers, camel case for parameters, when to use Pascal case for abbreviations (at least 3 chars -> pascal case instead of all caps) That makes it a lot less jarring reading and working with code from different libraries, yours just being one of them. Arbitrarily adding something to public identifiers (like classes that get an A prefix) makes your code not just look weird. Imagine everyone would think they’re above those conventions. Then everything would look different for no good reason, except different individual pet peeves of individual authors, but in the end most programs would look like a mess. Because they are an aggregate of custom code but also using a lot of 3rd party libraries. I for interfaces is mostly due to it inheriting a lot of stuff from COM, and lots of COM interfaces had the I prefix for decades. Interfaces are different enough in semantics and performance characteristics (they force structs to be boxed when used as interface), that having the I is a very good compromise. It’s also clear that you can implement them however you want, as opposed to abstract classes, that will always have some things wired in a way that cannot be overridden.
Indirection is an epidemic in OOP. While I personally stick to the style guide in C#, I do think that if you can't think of a descriptive name for your interface implementation that doesn't clash with its interface (without the "I"), then you probably don't need an interface.
Coordinate systems and loop variables are my exceptions to this rule. xyz, rgb, uv, hsl, ijk, etc. If you don't know what those mean then you probably shouldn't be messing with the code that deals with them.
Yeah, I take what was said in this video with a grain of salt. Sometimes, especially for variables with a short lifespan, abbreviating them makes the code a lot cleaner. Otherwise it’s just a mess with a bunch of variable names for simple operations
You can use abbreviations, even single-letter variables, just in the right context / scope. This is especially true with lambdas, which are often a single-line operation.
@@NihongoWakannai I would love to see any remotely large computation written with full words. Ah yes, easingParameter < 0.5 ? 4 * easingParameter * easingParameter * easingParameter : 1 - pow(-2 * easingParameter + 2, 3) / 2; is sooo much more readable than x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2; Especially given the entire thing is pretty much always fully encapsulated. It's just hardly anyone actually interacts with math at this level in code anymore. If all you're doing is implementing a textbook PDE computation or whatever, there's hardly any reason to invent new names.
Definitely, but even then I think you should ask yourself: is there useful information I could be conveying here? It made a surprising difference for readability when I changed, e.g. df.apply(lambda x: log(x.inc+1), axis=1) to df.apply(lambda row: log(row.inc+1), axis=1) Or something like for car_i, car in enumerate(cars): ... My feeling is, x means a number, i means an iteration index which really doesn't represent anything, and most other things should be named. I work a lot with code that models the real world, though. If I were writing like a crypto library where the vector is just a vector and it doesn't represent anything, I can imagine using single letters more.
@@mytuppenny On some level, a crypto library is less likely to use single letters because it deals with concrete implementations. You may be writing an abstract library with lots of real world applications, but heavily grounded in math - that's where most of equation-like code comes from. Or consider physics sims. Between E, emf, and electromotive_force, emf would likely the best option to use in code.
Back at university I shared my exercises with a friend who wasted an afternoon trying to figure out why I was multiplying something by 1.0. I spend the next morning trying to figure out my own code. I was converting an integer to a float. That's what the 1.0 was doing there. I will never forget the lesson I learned that day. Write in a way you will understand the code if you read it after 6 months. And comment everything without being overly explaining.
You can't comment everything without being over explaining - that's having your cake and eating it. Just use comments where you are doing something which is not that obvious, and a good way to do that is when rereading the code, if you spend a while thinking what does this do, then that's where a comment is needed. Less comments kind of make the ones that are important stand out. It's like councils putting safety signs on walls. Warning - these stairs are dangerous, make sure you are holding on tightly.
@@Andrew-rc3vh I once worked for a company where one person would write the code and another dev would add the comments. They'd just work through the code and any bits they didn't get after a moment's thought they'd add a comment. Pass it back to the original author to check they'd got the comments right (and rework the code where possible to make it clearer) then send it round for review. Worked pretty good. I tend to over comment my code while I'm working on it but I do a pass before sending it out for review where I strip most of that out - it was only for me as I was thinking through the code anyway.
@@xlerb2286 Well if you have a good naming convention the code itself becomes far more readable. I think the problem with the system you describe is probably the most important comments of all are the times where the code is unconventional and may rely on things some distance away from where you are commenting. It depends on how complicated the system is you are building, but without certain comments it can take hours to figure out. The other trap is if the comment says something different to how the system works, such as if the system was altered and the comment out of date. It's one reason I prefer to rely on reading the code than too many comments. Consistency is the number one thing which makes the code readable.
@ the brogrammers with the “good code speaks for itself” mentality: just learn to write a meaningful comment about what it does, I know it’s hard for you considering the advanced level of autism
Re units for variables: if your language has support for "newtypes" -- zero overhead wrappers for an existing type (like Haskell's newtypes, Rust's single-component structs or Python's typing.NewType) -- then consider using those. If you want a distance variable, then its type could be Millimeter, which is actually a u32. Then you can call it "distance: Millimeter" and not "distanceMillimeters: u32".
The newtype pattern is especially useful when you have a functions that accept multiple of the same type with different semantics. Like two different hash outputs that are both u32. It'd be easy to accidentally pass the wrong one. If you gave each a newtype then the compiler will stop you. Just like you were saying with distance. Also applies to time and a whole bunch of other things.
In my opinion it can make the problem worse. Now you have a new arbitrary type that you don't know what it is at a first glance. Is "distanceMillimeters" an integer or a floating-point type? Or maybe it's actually a new type with its own structure? Furthermore, this "type aliasing" can cause a type-hell where you start to do things like "intVector" instead of "vector" - this makes you question whether a function parameter is of a special type that contains some special logic. While it's sometimes a nice idea, it's not something you should do all the time (maybe except using enums instead of booleans, although this is an actual new type, not just an alias).
Not to mention that his font is tiny, which is not good for your eyes, and I often have multiple buffers side by side, with often a terminal thrown in, so having shorter columns can be good. I use pythons black's default of 88, which along with decent automated line-breaking (looking at you, gotfmt), works really well.
I tend to be wary of good practices that revolve around the words "always" or "never" because they reinforce dogmatism rather than an understanding of the conventions for coding. For example, advising to never abbreviate (or use acronyms for) variable names may seem like a good suggestion, but the main motivation behind it is that abbreviations introduce ambiguity. Therefore, I would be more inclined to suggest avoiding abbreviating when it results in ambiguity rather than objecting to it as a rule. I don't think I'd enjoy reading through code that systematically turns "id" into "identifier" for this reason. Or one that expands a well-known abbreviation from the domain that domain experts routinely use in an abbreviated form. On another note, I see a lot of value in making type names expose their abstract nature. Sure, from the point of view of client code, it doesn't really matter whether you're consuming an abstraction or a concrete type as far as API usage goes. However, this ceases to be the case when you need to start writing tests for your client code as concrete classes carry an overhead in that they need to be instantiated within tests. And within test environments they might be difficult to configure in such a way that the client code you're testing behaves in the expected way. Now, you might argue that this merely reinforces the notion that we should aim to express dependencies through abstractions, but establishing a convention around identifying pure abstractions with an easily recognizable feature (such as the leading I in C# interfaces) makes it much easier to identify dependencies that can be easily mocked within tests. It also allows code reviews to more readily identify sources of strong coupling between client code and a particular implementation of a dependency. Lacking a clear identifier forces the reviewer to need to look up the dependency to make that assessment.
I agree that there is value in making type names clear as to whether or not something is abstract or not but disagree with the Hungarian approach. This can lead to the laziness pointed out in the video of things like BaseTruck. Instead, things should be named based on how they're really abstracting something. Take these class names for example: Vehicle, MotorVehicle, Truck, TrailerTruck, or Animal, Pet, Dog, GermanShepherd. It's easy to identify is a class is concrete or not if the name of concrete classes are specific and the name of abstract classes are more general. The only thing this leaves out is if a class is an interface or an abstract class, but I would argue that this is less important than being able to understand a clear hierarchy as in my example and also that you're going to need to know more about the classes anyways in order to implement them regardless.
Yes, I use single letter or abbreviated variables all the time and it makes the code MORE readable. Any time I see people spreading dogma about coding style like this it makes me think they don't actually program and just regurgitate what they heard online.
From your first points, I disagree in that I think `ALWAYS`, `NEVER`, `MAY`, and `SHOULD` are all great, super important words for hard rules. You're looking at these rules in the video and I think the main problem is that the rules in the video are just plain naive and badly thought out. Well crowd analyzed ideas that include these words can be excellent, and a rule defined this way doesn't prevent being given context to understand it
For Python you can also use type hinting as a middle ground where the variable name doesn’t have the type and you get the clarity you’d have with explicit types.
I've been using type annotations in Python 3.8. In 3.8 they're kinda clunky (i.e. forward refs were added in Python 3.9).. they're still very useful. They help readability. Help you plan out functions (i.e. "I want to pass in these types of arguments and get these kinds). And they help Pycharm provide do type-checking, auto-complete, and find usages of a function or class.
@@jocassid1 if you feel like regular type annotations are not enough, you can do "import typing" for mkre such as Iterator, Generator etc. Pydantic is also a way to make your code simultaneously more and less readable at the same time
@@khuntasaurus88 "Pydantic is also a way to make your code simultaneously more and less readable at the same time" - so: import pydantic as quantum_pydantic ;-)
I think the reason why the IThing naming pattern is standard in c# is because you almost always want to deal with interfaces and not classes directly. Which means you always have to come up with two names. One for your class and one for the classes interface. Having a standard way of naming interfaces is, therefore, very convenient. It would also be incredibly confusing if you opened up a project where the interfaces had different names than their implementations even if where they only had a single implementation each.
Not that I use either but I think I'd prefer the way that it's done in Java (albeit less frequently, Java traditionally hasn't been as focused on interfaces as C#). Instead of IThing and Thing you'd have Thing and ThingImpl. It makes the interface you should use anyway have the "main" name and the default implementation comes alphabetically directly after the interface, making their connection obvious in most listings, be it of types or files.
@@swapode I think using Impl in the name of type makes it a bit of ugly, but I do respect your point. But, in defense of C#, prefixing interfaces with 'I' makes them easier to identify in code and even using intellisense, as we have both classes and structs in C# and having something more to differentiate interfaces turns the differentiation to be easier. It also helps with the naming of things a bit, as we can think on interfaces as they where simple units of contracts for specific things (or even traits nowadays), like "IDriveable" (it reads like, I driveable or I am driveable, you could also write IAmDriveable) or "ICanFly" or "ICachedDao", for the most part it's actually interesting. So instead of having interface Car and CarImpl, you can actually have the interfaces IHaveRoads, IDriveable, IGasBased, etc... and have a concrete type Car implementing these.
ThingImpl is a disaster. It implies that you will only ever have one class that implements Thing. It tells me that you have have been told to use interfaces everywhere and you’ve applied the rule without any thought. The c# convention sucks even worse. It has the same problem but the additional “feature” that everything in your public API starts with an I. That’s not great for readability.
Base, I-prefix, etc are handy in cases where as a user you may need to handle both the base class and the derived/concrete classes at different points. It's important to remember your API communicates with other programmers not just in the context of a single function, but in the context of potentially many different problems they are trying to solve. Understanding that something is a base or even has a base can help the programmer understand the API easier and know how to use it to solve later problems. e.g. I'm programming something that doesn't care about the given truck so it takes in BaseTruck, but that signals to me that there are several different kinds of Trucks potentially, so if my code needs specifically a DumpTruck I may be able to find it. It also tells me that I might be able to inherit from BaseTruck and make my own truck if the library doesn't provide 100% of the functionality I need.
Correct, he is absolutely wrong on this. Good, well written libraries that are intended to be extensible do in fact do this. His perspective tells me he has little experience or is not good at writing reusable code and libraries. Its a skill sadly most developers lack. The abstract class will be intended to be maximally flexible and allow developers to create descendants with new functionality. But such classes are often combersome for common use cases. So there is a default inplementation that is easier for programmers to use for typical cases. Thus something like VehicleAbs, TruckAbs and Truck as classes. His example of Truck vs TrailerTruck makes the case that he does not fully grock inheritance and good practices for reusable code because a trailer truck is NOT the same as a "truck". Also the use of I is quite handy. Often there is an interface and a class that implements it which share a name. The inclusion of "I" lets you tell the difference. Also makes it obvious when a junior or mid developer is usine the class type where they should be using the interface. One pattern I sometimes use is that the class is mutable for use in core code but the interface only provides read-only access. In which case having the interface named with an "I" is quite helpful. Also abbreviations can be fine if relatively standard and not abused. For example "Abs" for abstract if its used consistently does not at all harm readibility but can shorten some very long names.
I think there are often good reasons to keep names short, even single characters at times. This serves to show that the thing is of little importance and short-lived. For example consider this: button.onClick = (e: Event) => this.buttonClicked(e) In this case you know that e is an event, it is already given by its type and by knowing that you only assign event handlers to "onX" properties. The variable is also used immediately on the same line and never again. Naming it "event" or "clickEvent" or "click" in this case doesn't make your code any easier to read, but rather makes it bloated and takes your attention away from the important parts, which is the assignment and handler.
Using the words "always" and "never" invites lots of "what about"s. I think the guideline (not rule) should be "Favor readability over terseness". There are obviously some things so common no one would get confused; like using i for a loop iterator, using x and y for 2D coordinates, etc. I would say in the latter case, Player.pos(x,y) is much more readable than Player.screen_position(screen_x_coordinate, screen_y_coordinate).
@@BrianMelancon whats why you should never use "always" and "never". I say that sentence often and yes, the using of the word never is to underline the point :-D
I'm a big fan of verbNoun naming on functions, it is a great way to say what the function does, and in many cases, clues you in on what i might return in a preloading sort of way. Not just in terms of raw types, but in terms of the meaning of what it's trying to do. so things like a getMovie() function, or a getMovies() function, tells me I'm going to get back single objects, or likely an array. Another is to always name arrays and their ilk with a plural, and the singular instance with... well a singular. These aren't ground breaking by any means, but they go a long way to helping you write code that is easy to mentally parse with common structures, like for..of loops.
That plural versus singular idea gets really problematic really quickly, because if you had an array of Things and just one Thing , you’re only one letter off from an autocomplete or typo messing up your day. If one of something is getting called Thing, you’d be better off using another longer word to append after it. Back in the day they always liked putting the type or whatever abbreviated in front as mentioned in the video, because that’s how they taught us to write code in a VB Class over 20 years ago. I prefer flipping that around so I can easily see the slightly more thing first, followed by the descriptor. So, for an array of more than one Thing, I would go with Thing_Array or Thing_List, that way if I go searching for “Thing” I can see where I used both forms of it, just in case I forgot what the prefix I came up with was. Sure, I could put a wildcard before the word I’m looking for, but I like putting them afterwards because that’s how I’m built.
@@kernelpickle You're entitled to your methods but not a compelling argument imo. messing up your day being like messing up 30 seconds max since even just a linter should catch this, and this never happens to me despite doing this all the time. It would take longer to fix in dynamic languages, but even python has type hints. Autocomplete requires your input, if you need an array, you're already looking for plural, if not, you're already looking for singular. The autocomplete should not have to do your thinking for you. And definitely not a fan of the postfix hungarian notation alike. Document your function in the documentation, not in the name.
@@UnknownGamer40464 yeah, you're never gonna confuse a single object with an array, they're not interchangeable in code, your IDE will yell at you immediately
One problem with this is that some words don't have a singular and plural version. For this reason, I will name my functions getMovie() and getMovieList().
It happened to me multiple times that my code felt difficult to reason about, and then finding the right name for a class made everything much more clear and simple. Going out for a walk sometimes helps finding the name 😀.
I'm guilty of having a "Utils" class quite often, but mostly it's for when I have a bunch of fairly generic functions that I don't want to have 7 different classes each with a single function... Although I'm now moving towards having those classes, but putting them in a directory called "Utils" which is tidier. Speaking of which, I'd love to have a video on structuring projects in terms of the file system. I.e. what directories do you have, how best to split up different classes, that sort of thing.
agreed - sure, it's a clear sign you should ask yourself some questions 'is there a better place right now?' and 'how unwieldy/disorganized is this utils file?'.. But also: 'am I letting perfect be the enemy of the good enough?' ... but in my experience, the answer is context dependent. Many times the best organization is something that reveals itself over time and overthinking/overworking it now ends up being a waste.
Training (UA-camrs teaching) typically rely on contrived and overly simplistic examples because ugly real world examples often require compromise. Few really experienced developers would want to deal with the comments section of social media.
"Moving standalone functions into classes" isn't a very good advice... Or at least, it's a contraversal advice, because it directly contradicts with official C++ Core Guidelines by Bjarne Stroustrup and Herb Sutter, section C4: "Make a function a member only if it needs direct access to the representation of a class". A lot of times I see suggestions on the internet about clean code and coding standards that doesn't take into conderation any other existing documents and practices.
I think "never abbreviate" is a good bit of advice for someone addicted to it, but for some code it's fine. I'll name something x if it makes sense to, because I don't wanna write a 5th order polynomial where ever x is "firstOperand". This is the first thing everyone thinks with array languages, but terse notation is good. It's also important to make sure the user has the context for it first, and to justify the learning curve. Sometimes you can (I love array languages and maths-y code), but mostly you can't.
also, code comments are like, a thing, so if you want to keep the variable names short for convenience you can always just describe them in the comments, and there's no point having the names long if you ALREADY have to describe them in comments anyway
It's not about abbreviation, it's about context. If your context gives you enough information already, then you can abbreviate to xyz because you know what it's about. But if you are missing the context around it, then you should not abbreviate because this is obfuscating the code. The rule could be called: "never abbreviate context" rather than just "never abbreviate".
The super class having 'Base' in the name actually hurts a lot in the code I work on at work. We're in the kitchens industry and we have a thing called a base unit (or base cabinet in the US). So if you see a class 'BaseUnit' is that the base class of 'Unit' or is it the class representing a base unit? Well, the earlier devs didn't realise this so we have a bunch of confusingly named classes.
Reminds me of working with Joomla CMS. The convention there is to name ORM model/ActiveRecord classes "Table". Meanwhile, the application domain was publishing web content in HTML tables. Hence the not at all confusing name "TableTables" 🙃
@Ian Gregory this reminds me of some legacy databases I've sometimes work with. We have tables prefixed with the name of the database. For example, cms.cms_user, cms.cms_permission, etc. Makes me cringe whenever I come across those and I seriously want to write a migration to rename all those. What's worse is seeing column names following the same pattern where they're prefixed with the table's name, like user_firstname, user_lastname, user_username. AHHHHH! CRINGE!
I agree with most of it, but I find typed types pretty useful. I believe the "I" prefix for interfaces is quite helpful in understanding how the code is structured. For instance, I don't have to go to the source of a type to know it's being used for dependency injection when it's prefixed with "I" and within a certain context. I also know that to actually find what the calls to it do, I'll have to find implementations, not its own source. That goes a long way when navigating code I didn't write myself. In Unreal Engine, for example, you have T for template types (like TArray), F for structs and other prefixes that are helpful to know what usages, limitations and capabilities you can assume from those types without having to read their full source. Not to mention distinguishing things at a glance. E for Enums, for instance, so when I see EName::Value in C++, I know Value is not a static property of some class, it's an enum value.
I remember reading that Hungarian Notation actually came from one of the early programming leads for MS Word. The initial character actually had nothing to do with the type of the variable, it was meant to specify the type/units of the data. Was this a screen-space pixel? Maybe you have s_Width and s_Height. But for where you are on the document, you'd be measuring things in inches, so maybe i_Width (for inches) or d_Width (for document units). It was meant to inform the programmer about potential conversion issues that a simple 'int' wouldn't tell you. The other limitations of the time probably played a big part in them using small prefixes instead of writing out the whole words (or creating new types, like we could now). The real problems with Hungarian Notation came from other departments attempting to mimic the Word team, but not using (or knowing of?) the team's naming guidelines. I don't use Hungarian Notation myself, but I think it gets a lot of derision without knowing the original intent of the convention.
The very next recommendation after 'don't put types in variable names' was 'do put units in variable names'. Which, as you said, was the original intention of Hungarian notation. It's an interesting coincidence.
This. The original Apps Hungarian guidelines also specified to use "from" naming rather than "to" naming for conversation functions. For example, to convert from screen space to inches you might write: i_width = i_from_s(s_width); This made it easier to visually identify type mismatches because the prefixes on the left and right should always match.
Never heard of the term Hungarian Notation before. I prefix types into variable names simply for better readability of the code. It can be useful if I hadn't worked on a project for months and I have to look into it for some reason.
"the early programming leads for MS Word." Charles Simonyi. He was the lead for developing office (and is quite wealthy now got his trouble). He also has a PhD in computer science, so he's not just a business flak. I've read (and poked around in) C++ code that was a large ten effort that used Hungarian notation. It was pretty helpful, actually. Just because the type is declared *somewhere* didn't mean it's not way easier to determine it where you're looking in the code.
Wise words! I think the most important word in all of this is "guidelines". Yes, most of this makes sense most of the time, but there are times when deviation makes more sense (x,y,z coordinates etc). Another important concept is consistency - I hate to see numMovies and movieCount used in the same codebase.
Well it could get confusing if the programmer, for some assmad reason, had decided to use y for the 1st. horisontal axis, z for the 2nd. horizontal axis and x for the vertical axis. I guess one could technically name them horizontalX, horizontalY and verticalX or something to ensure less ambiguity even in contexts with "implicit" conventions like in coordinate systems.
@@kristianjensen5877 this does piss me off, in minecraft, your coordinates are x and z for where you are in the world, and then y is height. it annoys me, but i got used to it. i'll never do that in my code because no
In an oddly specific case, if you are using Applesoft BASIC on an ancient machine. You have to abbreviate your variables to 2 characters because the language differentiates and refers to the variables via their first 2 characters.
Commodore basic is the same way. I think they're both based on microsoft basic? so maybe a microsoft basic quirk. Also I think Ti calculator BASIC is limited to single character variables? At least on the old z80 ones?
When I started programming in 1978, BASIC supported two character names and C supported eight character names. That's why we used to abbreviate. It never had anything to do with screen width. But that's okay, the guidelines in this video are right on.
1. Abbreviations are fine if they're readable enough and widely understood. If it's a "everyone in the industry knows, but it might be hard for a beginner"-cases, I like to use docs (like the XML annotation in C#) to spell out the full name. Even though it's true that screensize has expanded, there is still a point at which length makes names annoying to read. 2. Totally agreed with units! Use a type of you can, a variable name, or at the very least add documentation. Don't make me guess! 3. Utils are contextual. In smaller projects, I like to keep a fairly general Util class with generic stuff like float equality checks with epsilon. Never had a problem with those. In bigger projects it's sensible to look for a more formalised approach... but the last 20 million line project I was working with still had a simple static Util class and that was one of the few things which just worked while everything else was a mess ¯\_(ツ)_/¯
2. i enforce this rule in my company. and additionally, if a variable or function returns a specific data type, especially if it returns boolean, state the variable/function name in a way the boolean would answer it like a yes/no question. E.g. instead of "checkAuth", write "canLogin" or "hasAccess"; if a function checks whether all systems are online, instead of "allSystemsOnline", say "areAllSystemsOnline" because "allSystemOnline" could be performing an action that makes all system online, but if a function is called "areAllSystemsOnline", then it is obvious that function is only going to perform a check rather than actually trying to bring all systems online. Also, please please please, bar some really rare scenarios, don't use negative words in names. !$user.inactive is just that much harder to read than $user.active. we are dealing with enough logical challenges already, no need to add double negative into the mix, ain't nobody got time for that.
1. True. In some cases single char vars are ubiquitous. Like the "i" in loops. Or short names for "exception" in a try-catch. But in custom cases like "durationSeconds" I'd strongly prefer using that name as the var name - as opposed to a short name and a documentation/comment. As less docs/comments is just...less source and fewer stale things that can become outdated
@@BenRangel yeah. if it's milliseconds though, then "durationMs" is fine for me, because "ms" is a very well established abbreviation (as long as it's obvious that it refers to time context / duration). relying on extensive documentation is fine if you're writing an API or a library. out of my experience, comments generally aren't read (even when they should be) - developers seem to develop some sort of a "comment blindness", probably because they're so often outdated or incomprehensible to begin with, so people kinda give up on them by default. i'm doing my best to avoid having to write any comments. if i find myself in a situation when i'm tempted to write one, more often than not it means the code needs to be rewritten. you need to explain what those 10 lines accomplish? extract those 10 lines into a well-named function; make that name take care of it. cases where comments are unavoidable happen sometimes - like a complex workaround required by a quirky bug in a third party library - but they're few and far between.
@@vibovitold Agree with everything. All devs are familiar with "ms" ✅ and docs are worth if it's used by lots of people, especially public apis ✅ I think it's fine to comment methods in a format that shows up as hints in editors. But wouldn't do it in my own teams codebase - I figure most of the time if the method name and args are simple and clear we don't need that comment. I don't hate them though. But I mostly use comments as a rarity to make people look carefully. I mostly comment weird stuff. Such as business decisions that seem wrong. Like "this format is obscure but we need it for marketing" (used to rely on git history for that type of stuff but after migrating between repos and file structures that kind of history can become obfuscated)
In my company we use Utils for misc functions and extensions which are used in mutliple projects. Something like calculating a hash for some data which just doesn't warrants making a complete single funtion class out of. And some Extensions for EnitityFramework like AddIfNotExists or stuff like that.
Thanks for making this video. I've seen a lot of videos that churn on for 15 minutes about a single, overengineered design pattern for "improving" your code, but these tips are super useful because I've personally suffered from a few of these mistakes quite a bit.
Verbs in function names are vital. For my projects, get should always refer to getting something from memory, which should not be confused with "pull" which might contain a get or a post request, "load", "write", "serialize"which contains io, or "calc" which contains some sort of calculation. This is key when it comes to something like a double for loop inside an update method. Saves hours during debugging phase.
@@fiona9891 depends, there is also a subtle difference there too: 'reqXxx()' implies the remote host may potentially reject the request, whereas 'pullXxx()' implies that the remote host only servers to provide information.
its beautiful, it reminds me that sometimes coding isn't always about producing but also about consuming. we hand our codes from one to another until it reaches the user.
This is the channel that was missing from my life. You can learn algorithms and practice and learn advanced techniques but it's like no one sits down to help you learn these kinds of things. Thank you so much, subbed.
I think an exception to the Utils naming is when the natural owner is a sealed class. For example in Java you might have a utility to get next midnight after a date in a time zone: public Date nextMidnight(Date after, TimeZone timezone). Ideally is you had extensions (like swift) you could just add it to the Date class - however in Java you can’t and are forced into DateUtils static methods.
Yeah this is fine imo I think the real problem is with names that are essentially just euphamisms for "miscellaneous" as in they have no qualifiers of what they relate to at all, like "utils", "shared", "common", "globals", "constants" Often these things contain a bunch of random methods or values, that could actually be organised but just aren't because the thought didnt occur or a good name was just not happening
I was going to say the same thing. If you're using a 3rd party library and cannot simply add new methods to existing types, this can be useful. In C++, I would go with a utils namespace, or perhaps a {module}_utils namespace, rather than a class.
I found it helpful to have prefixes to methods, getters and such that return booleans. For example: "active" -> "isActive". "needsUpdate", "isRoot", "hasTag", etc. Not a rule, but a guideline. Makes it easy to know exactly what you're asking for and what you're getting at a glance.
@@MichaelPohoreski Yes, but when the underlying language is English then I found the "verb" part (too) often be indistinguishable from a noun. English just has too many words that are identical as noun and verb: capture, run, start, stop, display, screen, filter, tag, mark, file, store, jump, find, loop, click, push, in-/decrement, in-/decrease, change, set, update, call, signal, message, ....
Ruby allows for question marks in method names so you can replace (for example) 'isValid' functions with 'valid?'. Really enforces the idea that you're asking a question and getting a boolean answer.
This is almost exactly the beginning of every discussion I had with one of my devs when we’d talk about writing better code. Having just retired from software development, it was a nice trip down memory lane! The next part of the discussion was always about managing (mostly by minimizing) dependencies in your code. This was more important than naming, but you always had to solve the naming problem first!
This might be the best channel on UA-cam. It hits that perfectionist urge and answers questions I didn't know I had about my code style. THANK YOU for the slick editing and soothing music. I actually pay attention and learn stuff!
Well, I wouldn't go that far, but Herr Jürgen* does agree with most of what is said here. *A mythical figure, a twisted meticulous nemesis hunting down bad coders.
It can be, but is definitely an overused name just like "Shared" and "Common" All work sometimes but if a more specific name can be given then that often makes code easier to read, packages easier to navigate and when you forget the name of the method but kept the class name simple gives you a much shorter list of methods to scan through when you're looking for it
What I would add is that good naming should first prioritize function names and arguments! The body of a function matters when you're working within that function, but the name of the function matters everywhere it's used. And when you are looking at the body of a function, the things it calls are some of the single biggest clues about what it's doing!
Lol, I'm watching this video in 144p, so when he asked us to see the difference between the two codes, I was just like "Yup, this is code. Idk what's written, but I bet it's good"
The most important thing is consistency. If you have a variable for the amount of cookies you have, there are so many ways to name it. Cookies cookie amtCookies Cookie_Amount Come up with a naming system that works with you, and it will avoid so many issues down the line.
Base and I prefixes in C# in my experience can be useful. Of course, they're easier to find because you can filter out derived classes and implementations, but they also indicate that whatever you're currently using is definitely something else, because you can't make an instance of an interface or an abstract class which a base class ought to be. It helps keep track of abstractions.
Maybe I'm biased coming from C#, but I find the interface prefix I-, and the base class previous Base- useful in understanding the contract I'm making with consumers of my code. Especially in TypeScript, there's a noticeable difference between saying "I expect an interface that implements this method" vs "I expect an abstract base class that defines these members" vs "I expect a concrete class of this type, or a subclass". This is the difference between passing curly-braces with a named function, a custom class that extends a base, or the need to use the provided types
I wonder what is video-maker's job. It all sounds great when we're talking about real objects, but when you get into more abstract object and business logic, it's not as obvious if you're expecting the user to implement an interface or just reuse some explicit class. I work in a space where the "I" or "Base" is very useful.
I believe according to Liskov substitution principle, it shouldn't really matter if the declared type of the received object is abstract or not - you'll receive an object matching the type's contract. I assume it matters while writing low-level, performance-oriented code, because then abstracting away implementation details is no longer beneficial. That said, C# is a high-level language, where I don't think it matters that much. From what I see, ITruck is not better in any way than just Truck, for they define the very same contract, the contract for the functionalities of a truck. Hence, I don't see how these prefixes help in any way to organize abstractions, for abstractions are all about their contracts, not about through which otherwise transparent language feature they force those contracts. Knowing what contract apply to the creation of the argument objects in my opinion is not the responsibility of the function. The function should perform its action on the object and call it a day. The contracts of how and where such objects can be created should be defined within the type itself and only be cared about during implementation of said class. Plus, in many IDE's, as in VS, interface names are even highlighted with a different color. All that said, I guess most people just got used to do things in a certain way, so it just feels right to them. When I wrote C# I did follow their naming conventions. After that, I carried that naming to my private Java projects, but there it felt like clutter and I reverted back to Truck instead of ITruck and never miss them ever since.
Yes, I and Base prefixes definitely helped while coding. Way better than those Java implementation classes adding Impl suffixes. And I still remember that I was dead trying to create an instance of List in Java. C#'s I prefixes just makes it so much clearer.
@@scriber36 While you are mostly correct in terms of contract handling by machines, humans don't think of OO code in terms of contract. They think of OO code in terms of types. Knowing this, providing as much useful information about what you're currently using as practical is great for making maintainable code. LSP and other SOLID principles make code functional and robust, proper naming makes it readable. As for syntax highlighing: there is way too much of it to just tell what it is at a glance. Color coding needs to be, well, decoded. An I in front of an interface tells you immediately and definitively that it's an interface. While I appreciate you sharing your experience of not needing them in your code, I am currently managing a 20,000+ lines project spread across 12+ microservices with up to 5 levels of abstraction of dozens of interfaces and base classes and hundreds of derived classes. I found proper, informative, distinct and consistent naming paramount in managing something like that. Also, while making a base class abstract isn't strictly necessary as per SOLID principles, it is very useful to signify that you must use a derived class if using the base class makes no sense, which is often the case in a complex hierarchy of multiple implementations.
I think the I-prefix can still make sense for interfaces when they appear as file names. It's arguable for sure, but I think having the name in the filename makes a big difference on the impact of the prefix.
We use exactly that - "I" prefix if it is an interface-class, "_t" suffix if it is a plain data-class, and very few other rules on naming stuff. It does make it easier.
I believe that the I-prefix is just a variation on the Base- or Abstract- thing. Like, a WeatherService and IWeatherService could be better named as GoogleWeatherService and WeatherService. I would keep the I-prefix in languages where the standard library uses that naming scheme, but still apply the more-specific class name (i.e. in that case they would be GoogleWeatherService and IWeatherService).
C is and was a statically typed language always. 2:35 It was not the language, but rather the lack of IDEs that give you all extra information of the variables at hand. The type as meta information was encoded in the name.
interfaces are prefixed because you usually have a concrete impl of the same name, also because of var it doesn't matter that it has a prefix - the interface is mentioned in like two places only
Yeah but his other point kinda covers that. You have an interface with the same name as another type, you should rename one of them. I used to think a little bit of hungarian notation was fine (I learned to program in game maker so half the assets in my game started with spr_ and obj_) but after learning a bit of Haskell and Rust (where the type straight up won't compile if it's not PascalCase, and variables won't compile without camelCase/snake_case respectively) I realized how unnecessary it is to ever use hungarian notation.
It's probably about convention or maybe something inherent in a programming language I don't know but if there is only one planned implementation for the interface (which is implied by the interface and the implementing class having the same name) then why bother with the interface at all?
@@fletchcanny717 Renaming them just for the sake would be stupid, you would be forced to come up with artificial names all the time. I'm so glad we're prefixing interfaces in C#.
@@lahodal if you're struggling to come up with good names for your interfaces thats a good indicator that it's a bad abstraction. So many problems are made easier without the complexity of polymorphism.
@@fletchcanny717implementation (interfaces) and inheritance (class : class) are not the same thing. his point of TrailerTruck : Truck makes sense because it's inheritance. interfaces aren't "base classes", they're literally not even classes, they're blueprints. you'd then have TrailerTruck : ITruck which is the same as Truck it but makes it super clear that ITruck is an abstract "contract" that might be a class, but doesn't necessarily need to be. these standards and guidelines exist for a reason
I use vim so the width occupied by the code still matters to me 😂 On a serious note though, sometimes variable names can be too long to be considered "more readable." My rule of thumb instead is: if that identifier still makes sense in the context of that block, then I'll use the least amount of words as possible.
As a firmware developer, prefixing variable names with datatypes is very useful. When I see a variable s16Speed, I immediately know that the variable can hold a max value of 32767 and a min of -32768 and having that info in the back of my mind can save a lot of time while debugging.
I started doing firmware in '84 in assembler and then C. I agree with you on that front. I've always been a big proponent of having a written coding convention/standard that can be passed along to new hires (others) that come into a project. I've seen WAY too much code where this doesn't exist, and 'the next guy' comes along and just does whatever they feel like doing. The result is never good.
From my experience translating programs from one language to another or reviewing old code that I remember almost nothing about, it helps a lot to add the type. And also use types that specify the size, like Int16, Int32, etc.
I'm an embedded engineer too and I absolutely loathe Hungarian notation. I've never found it useful, and what I have seen is type changes made without updating the var names. But when you DO update the var names, then suddenly your code commits have noise in them, likely obscuring actual changes. Never used it, never have regretted it.
@@marccygnus I haven't worked with C/C++ etc. in years. We never used type in variable names. We also never wrote functions/methods that were so long that one would get lost in a mess of code and not be able to see the declarations. I gave up on writing comments except for those special outlier cases or for API/interfaces. I can't tell you how many bazillion lines of code/comments I've run into that didn't match due to changes.
I actually do abbreviate names in certain rare instances of writing shaders. I write water system using Gerstner waves. I link the formula I use as a comment and then I use exact same names as math counterpart. I find it really helpful for complex math.
Yeah that checks out. It seems that everyone that writes shader code is afraid to use a variable name more than 3 letters long xD. Makes reading shaders you didn't write a pain in the ass. (but yeah for math stuff like you mentioned it is very helpful to make the math and variable names line up, so that gets a pass)
I think abbreviation has advantages. Smaller names occupy less visual space on the screen, making it cleaner. Very big names makes the code harder to read, in my opinion. Some abbrevations are completely fine, like "std" for "standard", which is commonly used. Also, in the example following, you use "NumUsers", whici is another good abbreviation.
I agree. Not using abbreviation is only good for relatively simple class, but not when you need to get data from member of member of member of an object, and all of them have long names. Also not everyone has 4k monitor. I'm still working on my 1080p laptop and a sub 1366x768 monitor.
Eh, it makes sense to abbreviate if the abbreviation is more canonical (like "i" in for loops) but gone are the days of 80 column displays. We have the aspect ratio to display slightly more verbose names, and I'd much rather read code which has too descriptive variable names than too short names
Abbreviations are mostly bad. Except in such cases as the abbreviation is so well known that it's effectively a proper noun. It should be common enough that even if the reader does not know it, they can pop it into google and the first result is the definition. I would definitely just call it "Users" with an integer type. Unless I also want to also work with an array of users, in which case it would be preferable to do len(Users) instead of having it as a separate variable.
Great book. But kinda feel like every dev has to go through a journey to become interested in best practices of refactoring. And some never get there. They're fine with "it works". And some that learned it in once might forget bits of it over time as they get stressed over learning new tech 😔
I think the single letter variable names came from a combination of low memory machines and 80 column wide monitors. Also, some exceptions to the no single letter name rules are: i, j, and k in loops and things like x, y, z; u, v; or r, g, b; etc... Any of those axis labels in coordinate systems.
You are right about the origin of the single letter variables and that's the same reason hungarian notation was around, as an helper, for old programmers the letters aren't gibberish, for newer programmers they definitely sound like that. 'strCwd' sounds like a seizure but an old programmer will recognize that it's a string representing the Current Working Directory. We don't live in a low memory world anywore and we should work ourselves up to write good variable names for the future. As for the axis, x_axis would be better in my opinion, or even horizontalAxis. As programming progresses many people that aren't from the field approach it and many fields outside programming acquire programming in their daily schedule, if a math major finds an x he might solve for it, if a pirate finds an x he might start digging, if I find an x, I would likely hope she doesn't notice me because it would be weird. In short, you should make sure that every other person reading your code who isn't in aware of your context can understand the code! :)
@@impromptu222 I can see your point, but I would still say thay things like coordinate system labels are fine as single letter names. If, for example, you're in the code that deals with uv coordinates and you don't know what uv means then you probably shouldn't be messing with my procedural mesh generator. I wouldn't want to work with a vector library that named the axes horizontalAxis, verticalAxis, and depthAxis instead of just x, y, and z. I also wouldn't want to work with a colour library that used full names instead of rgb or hsl, etc. Sometimes the single letter names have a we'll known meaning in a particular domain. In which case the single letters are fine. I'm not saying it doesn't make it harder for new people to said domain to understand what the variables mean, but even having full names in those cases probably doesn't make the code much easier either. But, before we have a disagreement about something we don't disagree on, let me reiterate I agree that in general full variable names are a good thing. Especially when you work with developers whoes first language is not English. Abbreviations may seem obvious to some, but full words enable easier translation.
other use for single letter variable name: variables that have no real meaning for a dumb, simple example, compare: add leftAddend rightAddend = leftAddend + rightAddend add x y = x + y
Actually: this video is phantastic: exactly the right length, very good examples, concisely explained. Wonderful. I personally like refactorings that end in deleting unnecessary stuff.
There's one time that I put types into my variable names: When I have two variables that differ only in their data type. Like if I read in a string that represents a number, and then I convert it to an integer. I'll sometimes call them stringQty and intQty or some such.
Super useful !! just when I thought I wouldn't learn anything new this opened my eyes; I have a tendency to use 'Util' classes and always thought there were ugly but did not know what to make of them, this will help me get that sorted out, thanks!
Only half-agree with the 'no utils' rule: yes, put functions close to where they make most sense, BUT also don't unnecessarily pollute Movie class with quirky business logic that you'd rather evolve separately from the class itself. Also not everything has to live inside an OO object, take inspiration from how functional languages organize data and code separately -- but close to each other where it makes sense.
You are right... I think the rule here is that if you have a Utils class, then all its methods must be static. If one of you methods needs to persist state, then you need to have a dedicated class for it, or should go directly to where it makes sense
Ok, I just watched both of your videos and noticed those were all of them. I'm amazed at how great your content already is, those are some good tips and the vibe and quality of your videos are amazing. I'm definitely subscribing and keeping an eye on it for more!
These videos are perfect to put on when you go to sleep; You close your eyes, listen to the background music and his voice explaining nicely that we all suck. And at the end we even keep the information in our head because they are presented so calmly and clearly. 🤣
4:36 Python does it. The class that all exceptions are derived from is known as BaseException. As far as I know, this is the only example. It has a subclass called Exception, which is used for all actual errors, whereas the other subclasses are more just for "edge cases", e.g. KeyboardInterrupt, which is raised when you press ctrl-C
Java also does the same with some of its stock libraries like Swing (for desktop UI), providing base abstract classes starting with "Abstract" in their names. Funnily enough, it does not do the same with exceptions, instead defining "Throwable" as the base class, deriving "Exception" and "Error" from it, and using more specific names from there onwards like "FileNotFoundException" and "OutOfMemoryError".
This has a reason. It’s to prevent catching SystemExit and KeyboardInterrupt when someone tries to catch Exception. Though name could have been better.
3:33 while Peyton has no formal type declarations, it does have type hinting. Something which is considered good practice to use in modern python code.
@@isodoubIet That’s only true if you’re using a basic text editor. The real strength of type hints comes when they are used in conjunction with a modern IDE or code editor which it can read the hints and give warnings when you’re using the wrong datatype.
counter examples: when something is a unit of time or distance among other variables of the same units names like "t" and "x" are ok. abbreviations: when the abbreviation is obvious and saves a lot of space it can sometimes make code easier to read, not harder. having more characters on screen also means you have to take more time to scan that same screen, i started programming long after the days of 80 char terminals, but i don't think that just being able to fit more on a screen justifies longer variable names "don't put types in your name". Hungarian notation really pisses me off and i hate it so much ... That being said, sometimes boolean variables might have a name that doesn't work as is* and it's easier to name it as "b_*" units: when your whole program is small and uses the same unit, i.e. milliseconds, and you know that's as specific as you'll ever need to be, or at least it will be in 90+% of the variables, it's ok to omit the unit. prefixing with "I": i have nothing for this one naming something *Base: suppose the derived class is templated and the base is not, that could be the reason it's been split into 2 classes, and perhaps the derived one is the one that will be more commonly used. utils classes: this seems like an argument of OO vs procedural more than an argument about naming. Many times one may want to create simple reusable functions that deal with basic data types, and put them in a namespace like "string_util" or "math_util", or sometimes even just "util" if you can't find a better group for it. The idea functions with even only a single data type should only exist as a method is an unfortunate perception of OO design, it might not bother you until you realize that it would be really convenient to have a "bool startsWith(std::string, std::string)" availiable in a lot of places
This should be pinned I also thought about "Node", a very general type/struct name (it doesn't tell you anything about what kind of structure it describes), yet if you use only one data structure in your program there's no need to be overly specific, generic names aren't always bad
@@r.t.5767 Thanks, agreed on "Node" and similar short names. If you have a program with only 1 kind of node there's no point in making an over-long name, and if you add a different data structure in the future you can always just rename Node, either with macros if you're using vim or with a refactoring tool
When you’re developing in an application domain with commonly used abbreviations, such as x, y, and z for spatial coordinate axes, you should use its abbreviations. This includes code based on mathematical formulas or theorems, where, yes, single-letter variable names are common.
Math formulas and theorems usually don't use _names_ at all. This is a common confusion between the programming concept and (esp. w/single-character variable names) the somewhat similarly looking formulas and _symbols_ used in mathematics and science. A mathematical symbol is simply that, a symbol, but of which a single character may well serve the role. It does not represent a storage location as such, for example. It may represent a numerical value, known or unknown, but not something that changes over time in general like a variable does. This is an abstract concept. An English word doesn't easily serve that role because it usually consists of multiple symbols (i.e. characters), and each of the symbols would represent a related idea/concept. Perhaps it is somewhat unfortunate that they appear similar at first glace?
@@isawadelapradera6490 Their proper, full names are "abscissa", "ordinate", and "applicate", but that usage dropped tremendously during the XX century; but my 1956 book on analytical geometry still calls them that.
b), I like when it can be read like a sentence: if (machine.isEnabled) ...
15 днів тому
The "I" before an interface is a MUST BE when you get to work on really big projects. Sometimes you need to find for a proper interface, and you dont even know if it exists! The fact that inside your code you can have either classes or interfaces, and to make them separated with this, is really useful and a lot of time saver. Also, you said that to the user it doesnt matter if its an interface, and thats essentially not true. When you see the "I" prefix you know at first glance that this object has only functions inside and you wont be able to get variables if you dont cast them to the proper implementator.
Something a coworker (who was like my lead in a couple projects, really amazing dude) suggested was “name variables in a self-descriptive name” a single simple rule that really made lots of sense, e.g. if your variable would fetch the size of an array then it would be something like “arrSize” or “arr_size” (depending on which case you using)
Your channel is awesome and I can’t wait to see it grow! One tip is possibly lowering the music volume. It was slightly distracting in my opinion and I totally get that it takes some fine tuning but figured I’d give a little advice if it helps!
@@CodeAesthetic honestly, don’t be afraid to just get rid of the music. Music for the sake of filling sound can become more distracting than anything. Have it at key points if you want (it helps make an impact) but let it fade out during explanations. Also, you voice alone sounds nice and is nowhere near the grating others have that need music to drown out.
@@grepgrok8735 Complety agree with you. Found myself way more confortable at 6:40 whem the music stopped, even without voice, but coding in the screen. Apart from that, really good content, keep it up!
This video is amazing! I really love the production value! Hearing others just talk about code is very inspirational and motivating. Please don't stop making these videos!
The I in front of the interface makes it instantly clear that it’s not inheritance but implementation, and when creating a class that can be used in a function call, it gives a great hint at the programmer that they need to implement that function. I absolutely love that C# coding convention. Some conventions are just plain bad, I agree, this one isn’t imo…
I sometimes still use one-letter variable names to store a return value of a function that I don't intend to use, just as a visual clue to myself. I write a lot of small, special-purpose data analysis or protocol simulation tools, so I tend to use huge block comments with the input and output data listed, packet details, etc. with a detailed explanation of the motivation for why such code needs to now exist.
Motivation of class purpose is pretty good and often omitted habit. But in large projects I'm usually lost in how classes interact with other and where to put/find some utilities that interact with two classes at once and do not belong to neither of them. Or that extend some functionality, but that functionality does not belong to low level code and might be application specific. To many levels of inheritance is not great. I came to conclusion that all the best practices are sometimes harmful when taken to the extreme and they may work in academic examples.
Calling abstract base classes "ClassBase" in C# is a useful practice because it tells the person consuming the class that there are abstract members which they will need to implement. The only time the class should be references is in its derivations and if you want to use it as a parameter you should probably use an interface for testing purposes. In C# at least, there are very good reasons for the naming conventions.
That sounds weird, because the IDE and the compiler warns and shows errors about required implementations of abstract methods anyways, and if you want to extend a type, you are more than likely had a glance at the type's definition and saw whether it's abstract or an interface. Or just have your cursor at the type name and see it in the shown info I guess. Not to mention you can create abstract classes with all methods implemented, so in that case implementers are actually not required to add anything more. Another way to approach the question is, why do you need a Base class, if as you say, you will use the interface for testing, hence you have an interface anyways? Sounds like (from C# 8) the interface could have default methods, instead of putting them into a *Base class. I've only ever have written *Base classes for this reason, and I don't write such base classes in other languages ever since. In all other purposes I guess, like to enforce some behaviors e.g. some logic in the constructor, I'd just call them their ordinary name and not *Base. I'm happy to be corrected, but I don't see no point for such abstraction layer between the interface and the concrete class, especially since default method implementations for interfaces are possible.
Ah yes, i remember that day when i figured that C++ compiler I was using could process variable names in Cyrillic, and Used it to obfuscate code for my teacher to be unable to read it. Like, naming one variable "c" in english, and other "с" in Cyrillic 'n stuff I could have this line: int xp = 10, хр = 0, ko = 15, ко = 0; And it would work
I really dont understand most of what you are talking about, but just like I learned english by watching youtube videos for 2 years until I finally was able to understand the language I will keep watching your content until your words start to make sense for me
Sometimes, Utility class is unavoidable for normally inaccessible classes. For example, in Unity game engine, you need to extend camera, UI, or vector classes to add more functions to be more productive. Or you want to make localization easier, you need to extend string class, so you can use string.TranslateToAnotherLanguage() etc.
This video is so beautiful to watch holy shit, are you guys not amazed by seeing code, CODE, being portrayed like it’s a cinematic clip. The clips of code, the way what he’s talking about appears or disappears or gets underlined. The familiarity of format of VS code. God it’s so satisfying to watch. Been coding for a while now but never say videos like this, definitely recommend more channels or even one of videos if you know any.
Just want to say that your music in this video is perfect. It melded together great with your presentation and I don’t feel like I missed any of the points.
Pls make more videos and don't give up on this. You're incredible! Your videos are so relaxing, your voice, the speed you're talking with, everything is perfect to me. I can even donate a little bit to support you. Btw. What's the name of the background music?
I work in a highly frustrating codebase where many JS variables are named like "strRowId" and then it doesn't actually contain a string. Or, the variable name is prefixed with 'int' and then it's a string, instead. Or, prefixed with 'obj' only to realize that it's an array (and vice-versa). Additionally, when things fail validation tests or have no value, they are often times re-assigned bool(false), instead of a value akin to its supposed type, like null, or 0. It's extremely nonsensical. Please don't do these things to yourself or others.
In most cases yes, but say if you are working on a voxel engine where you have world coordinates and chunk coordinates, it’s nice to specify in the functions that it’s looking for xChunkPos, yChunkPos OR xWorldPos, yWorldPos
Plus possible local positions within (child) components. But in general I tend to go more with "globalX/globalY/globalZ", "chunkX/chunkY/chunkZ" and/or "localX/localY/localZ". For rotational information I tend to stick with what I'm used to from CNC machines and apply the same "syntax" with A, B and C for the rotational axis. Like that there's really no way to mix them up and whoops by that we accidentally made a vector able to describe any given position and orientation in 3D space while mostly sticking to industrial standardisation xD@@blockify
I think the Hungarian Notation actually stems from the oldest programming language; Fortran. In Fortran, there exists implicit typing, where the first letter of a variable can imply a specific type if it's not otherwise declared. So you end up with a lot of variables like "icount" which would be implicitly assumed to be an integer because it starts with "i".
No. It actually comes from a Microsoft developer, Charles Simonyi (who's Hungarian). They were using a language (BCPL) without types, where every variable was a 16 bit word. He wrote a whole whitepaper on this. As the video stated naming variables is hard, so he focused on creating a system for it. Including the type in the name is just one thing in there. It's not needed, if you're using an IDE to read code, but sometimes you don't have access to one. It might still be useful to see the type of the variable without having to scroll back to the declaration.
Hungarian notation, when in a typed language, was also originally designed to tell you the parts of the type that the language didn't understand, like the units. It's an integer, but is it measuring vertical pixels, horizontal pixels, vertical points, or horizontal points? If you see (hpxMargin+hptWidth) you know it's wrong. If you see { vpxIndex = hpxIndex + 1 } you know it's wrong. You can't add pixels to points and you can't change horizontal into vertical. So instead of just a float, you have "milesDistance" or "kilometersDistance" or "altitudeMetersCurrent" and you'd treat those as different types, even though they're all three floats. (But of course you wind up abbreviating the prefixes.) In languages like Ada with better types, you can actually say "make a new type that's an integer but only interacts with other variables of this new type" so you don't have this problem.
@@darrennew8211 Properly, there are two types of Hungarian notation, Apps and System Hungarian. Apps Hungarian encodes the semantic type, like vertical position, row, zero-terminated strings in languages that don't have those as a type, etc. System Hungarian encodes the programming data type, like int, string, pointer, etc. Apps Hungarian is still a good idea, but less necessary now that we have modern IDEs and can cheaply create types to indicate vertical position, row, etc.
@@darthbob88 I think unless your system enforces "newtype" (i.e., you can't interchange float miles with float kilometers) app hungarian might still be useful. Modern IDEs are definitely helpful compared to when Hungarian was introduced, for sure. I can imagine no reason for system hungarian notation even back then, assuming your language was statically (and hopefully strongly) typed in the first place.
About the 'I' in front of interfaces: The reason is not to tell the user that this is an interface. The reason is to avoid name clashes with the actual implementation. Most of the time, you define interfaces for dependency injection. A service declares dependencies. But very often, there is only one implementation for the dependency anyway, the interface is just if you want to replace the implementation in the future, or for mocking in tests. So we very often have a Logger or FileSaver interface, and then an implementation with the same name. So you _have_ to make _some_ distinction between the two. Whether that is "FileSaver : IFileSaver" or "FileSaverImpl : FileSaver" really doesn't matter, and in that case I would just conform myself to the framework that I am using. In dotnet, it is the former approach. However, I do agree that it does not make sense to prefix an 'I' to interfaces that describe _capabilities._ Like "Movable" or something like that.
It's also just a single letter that helps you differentiate if what you are looking at is a class or an interface. I'd say even if you could instantiate it directly. Costs little and adds to readability. And helps you know that what you are receiving is any class that implements a functionality rather than a class that inherits.
At least for Java/Kotlin (and maybe other JVM languages as well), you can mock concrete implementations without a problem, even if they're final. I avoid creating interfaces unless I know there will be multiple implementations.
@@cotton9126 This! Using a capable IDE it is so easy to extract an interface later, only when it is really needed. Blindly creating single implementation interfaces just adds a lot of boiler plate for no real gain.
This is a really good guide. However, I'd like to offer a story of mine realted to 6:00 (utils) Working in C#, I have actually found myself making a bunch of Utils classes. I've ended up doing that because I want to try and keep things organized and in a place where others know where to find certain things. So I might have things like "MathUtil" "GeomUtil" "RandUtil" "PhysUtil", so if somone is looked for a particular static function for a thing, they can find it by doing a class search in VisualStudio for "util" and find everything. But also, a second reason for having so many Util classes is because in some cases, I obviously can't "make a new class" like mentioned in the video. So for example, I might want to make a special mod function for either int's or floats, where the negative inputs give positive outputs. But I can't just "make a new int class" in this case. The best thing I can do is make extention methods, which is still what I do. But that means I'm still going to have a static class dedicated to holding these functions. And I might as well still append "Util" to the end so it's easy to find. I could use namespacing so I can just call the class Math, but I feel like that just becomes even more of a rats nest of a problem with potentially having now 3 different Math classes I might want to use at the same time. But ultimately, I'm not writing any of this to say my way is right and it's a water proof system. I'm writing this because I want to hear your (CodeAesthetic) or anyone else's approaches to the problem. Because trying to avoid utils for classes that you can't have edit access to is an important exception to consider.
Personally, I only create a Utils file/class when I have a handful of disparate functions that don't fit anywhere else better, but also aren't "chunkable" enough to be worth making into their own files. One of my recent Haskell projects, for example, has a Utils module containing things like: - A ?: operator that mimics the one from Javascript with Haskell's Maybe type - An inverse function composition operator - A function to get the value from an Either where both sides are the same type - Versions of the fold function that terminate early under certain conditions - Basic string manipulation utilities - ... and so on. None of these things are large enough to warrant their own module (most are single-line functions), nor are they related enough to warrant splitting away only a handful, and they can't be moved into any specific context where they're used because they're used all over the place. And so, into the catch-all Utils module they go. If and when things start to accumulate in that file that are closely related enough to warrant their own module, I'll move them; for now, it's not needed.
Yeah, in C# I almost always have a class called Extensions. 90% of the time I'm not even using the class directly, so I would just do (5).Method() and the fact that Method is in a class called Extensions is of no concern at the time. Then when someone is reading it or needs the extension method it is extremely obvious that the extensions class holds it and you just need to use that namespace.
The number one thing I tell new group members on student projects is to *use positive boolean names*. It is very easy to get wrapped up in chained negations when programming and in my experience it helps immensely if all of your booleans (and other types too, for that matter!) are named under the assumption that "true" describes a straightforward state, even if that state is different from what the variable is expected to evaluate to. For instance, "isHidden" is suboptimal because it inverts the concept of visibility. Even if an object being hidden is its default state, you should use "isVisible" instead to make it easier to parse what is being checked for. And be consistent with this rule! I've found that just the one inversion can make your logic a mess. Here's another example: "isNotInside" can be improved to "isOutside" -- but if you're describing an object that encloses a space, it might be more semantically meaningful to track what's *inside* the object. You can (probably) just name the variable "isInside", thereby keeping the apparent internal logic of the object straightforward. I think the I-prefix on interfaces might be due to how how C# is kinda the sanitized Java, and Java in turn is kinda the sanitized C++. I don't really know what the chain of logic might have been, but it seems like it had to do with how Java ditched multiple inheritance and low-level controls and C# tried to claw back that space a little bit. Perhaps it was felt that multiple inheritance being messy in C++ was something that could be solved partially by properly separating interfaces and classes. I'm guilty of doing the BaseType thing, and it's probably a habit I should break, but I find it signals effectively that the class is not for general use. Remember the lessons of self-commenting code: signal your intent. Calling something "Base" implies not only that it is abstract, but also that you should use that particular class (rather than any potential superclass) as your foundation if it matches what you want to do.
Ha! And here is the comment I was watching the video for. Because I generally use positive Boolean property names, but wondered when I had a “hasConstraints()” function. Because it is used in a heuristic, I am only interested in a decisive “false”, while a “maybe” and a “yes” can be merged in the “true” return. So for this particular case it would be more straight forward to make it “isUnconstrained()”. 😅
Re base types, maybe it signals that the class is not fit for _any_ use! 😅 All jokes aside, I am guilty of this, too; and although I disagree with some of what was said, in this case I plan to change my ways.
@@creativecraving I don't think I will, personally. I worked at a place where the convention was to have subclasses share at least one word with their superclass whenever possible. So the abstract class might be called WindowBase, then there would be a concrete Window class, and that one might subclass into a DualWindow class, which in turn might be the base for a DualScreen class. Okay, so this is kind of a messy example. But it made sense for WPF stuff. The point is that it was easy to tell by sight if there was any inheritance going on, and made naming easier.
Regarding variable names inside functions. I found that if the function is pure and adequately named and is concise (does not aim to do 50 things as once), then the variable names can go ahead and have single-character names.
I appreciate the `delaySeconds` callout. It aligns with my thoughts on unrefactored code where I will prefix things with a "namespace" or larger container first approach. ie `delay_seconds`, `delay_minutes`, `event_click`
Single letter variables do tell you something about the variable. They say "I'm not important", "my scope is as small as I am", "I'm fleeting" and "I'll be gone in 2 lines".
Yeah exactly. The more imposing the variable name is the bigger the scope. Since letter variables are for a few lines of code in the middle of a function. Large screaming snake case constants are constants that span 1 or more files.
Still doesn't justify using i instead of index, or using i, j instead of rowIndex, colIndex. or k,v instead of key, value, or......
you catch my drift.
@@LeeorVardi I think it does!
@@xcraftminebb why?
@@Daren6111 as the original comment says. Just for very simple variables which have no meaning other than iterative variables like “int i”, or “char c” for one line variables. Or, like lambda function parameters (k, v) for map’s. Though I do see value in naming row / col.
There are only two hard things in computer science: Cache invalidation, naming things, and off-by-one errors.
off by one errors are like trying to plug a usb into its slot. You always bugger it up first try despite doing the same thing forever. Sometimes a third attempt is needed somehow 🤣
I disagree. There are three hard things: Cache invalidation, naming things, preventing buffer overruns, and hunter2
I disagree. There are three hard things: Cache invalidation, naming things, and developers agreeing.
There are only two hard things in computer science: Cache invalidation, naming things, and off-by-one errors, and cache invalidation.
secondthead orz
“Abbreviations rely on context you may or may not have.”
What a perfect summary right in the intro.
I ran into this today, got a piece of old Amiga C code I wrote as a teenager 30 years ago and couldn't for the life of me figure out what it meant. Really glad I've stopped doing that later in my programming life lol!
And there were many other bad habbits like experimental code that's left in the code base without comments or documentation, it's no longer used. I kept it as a repository so that I could refer later to it. There was no version control back then. And GUI, util, handlers all mashed together AND spread apart in multiple files. It's a total mess! No wonder that the code was in a half baked state. I've managed to get the base functionality working again, thanks to the debugger, which I didn't understand how to use back then.
Another mistake I made back then was naming functions inconsistently like CloseProjectWindow vs ProjectWindowClose and confusing the two. And then using PICloseWindow, using an abbreviation I don't remember and ordering it in yet another way.
I bumped into this when trying to modify an open source game, but I couldn't figure out what anything did because all the variables were incredibly vague abbreviations.
If you don't have context, you don't have enough information to mess with the code.
Even with context they can be hard to read, and without context they are impossible to read
I do think he should have explained why code (in particular old code) used abbreviations. Maybe because it required less typing in a world without intellisense? If you consider the function DefWindowProc (in the Win32 API from 1995), does "Def" mean "Default" or "Define"? Does "Proc" mean "Process" or "Procedure"? The function header is:
LRESULT DefWindowProc(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
What is an "LRESULT"? What is an "HWND"? What is a "UINT"? What is a "WPARAM"? What is an "LPARAM"?
"HWND" is a WindowHandle. A handle is the ID of a resource managed by Windows. Not a pointer (so nothing to do with physical addresses), just an ID that identifies it (like a social security number identifies a person). "WindowsId" is more descriptive than "handle", but the word "Windows" is overloaded because it could be talking about the operating system or an actual window. So "WinId" might be better than "WindowsId". So maybe "HWND" could be "windowWinId" instead?
"UINT" is an unsigned 32-bit integer. Why not call it unsigned32bitInteger? If the 32 needs to be flexible (maybe 64 or 128 some day) then it could be unsignedNativeInteger? Integers are common enough that "uint32" would probably be an okay abbreviation (the fact that the value 1 is physically stored as 15 zeros, then a 1, then 16 zeros is a whole other can of worms but I'll ignore that for now). And how is a "Msg" (maybe "message" would be better for the name for the parameter) which is usually something human readable that is meant to be read by humans just an integer in this case? Is it an instruction to the window? Maybe "windowInstruction" would be better than "Msg"? Even in a world without intellisense, a lot of abbreviations make no sense because it takes more effort to remember the abbreviations than it does to type out non-abbreviated versions.
But... BUT... abbreviated code fits nicely in a 320x240 screen. So, we should all use abbreviations like "WPARAM wParam, LPARAM lParam". 640x480 killed the need for abbreviations. But it was just "the way things were done" so the practice went on for a long time after 640x480 was mainstream. Now a days we even have 800x600 so... yeah...
I personally wouldn’t go that far. While abbreviations like URL and DAO do require some context, I’d rather write ImageUrl or somethingDAO rather than imageUniformResourceLocator or somethingDataAccessObject. Somehow these names are less comprehensive than their abbreviations.
Retired engineer here - started with Fortran with punched card decks and of course assembly code. I remember when "real engineers could do everything in 8k of core". Every byte was precious. It was beat into our heads to write tight code. As a young engineer in the late seventies I recall struggling with code maintenance and reuse. I shared my frustrations with another engineer who asked a key question that changed everything for me. He said something like "when was the last time you had problems fitting a program into memory?" I couldn't remember. Our newest designs had faster CPUs and more memory. After that I programmed for readability and reuse. Execution time critical or "real time" code was kept separate and avoided when possible. Thanks for sharing these tips with others.
Embedded systems engineer here. The last time I had trouble fitting code into memory was yesterday.
Instead of asking about fitting programs into memory, we should asking about AAA games from multibillion dollars companies having stutters, fps drops, fitting on SSD, fitting into memory with Discord, MSI Afterburner, OBS and browser running...
And the answers will be today, all day, everyday.
Not the program faults. Ask the management
@@frankfrei6848 You two had different tasks then. Some programs might not need to be optimized to perfection, but be readable instead.
I was just a bit later than you in the technology curve. But tight code was still important and it's a hard habit to break. Even the variable name would take up space and we'd think about what branches of the code would be called most and put those first. I just started programming python in an IDE and, wow, what a difference. The video's thoughts about long variable names is spot on. The IDE suggests/fills in the name and if you do get a character wrong it highlights that the variable isn't being used elsewhere, no need to wait for compile or runtime errors. And to think that back in the day we avoided interpreted languages due to their slower execution times. Kid's don't know how good they have it!
The "if you're having trouble naming something, it probably means something needs to be restructured" rule of thumb is honestly my biggest takeaway from this.
Or... It's 3am and you're too tired to name the variables hahah
@@josk8936 When you look at your code the next morning and start finding variables named "fuck_it" and "iGiveUp", that's your cue to maybe sleep more lol #soivebeentold
@@mk_jdilla Is it an integer because of i prefix? 😂😂😂
@@marcotroster8247 bYes
Many times refactor cost too much. Find more solutions that will prevent refactoring.
The actual version of the starting quote should be:
"There are only two hard things in computer science: Cache invalidation, naming things, and off by one errors"
hah
Off by one isn’t hard because, well, it’s off by one
Top comment.
I've heard this quote be a hundred different things, only "naming things" seems to be consistent
@@evanbelcher because that's the joke most of the time
"There are only two hard things in CS
[literally anything], and naming things"
Gets to the point immediately, concise and clean presentation, not too long, speaks clearly, ends when the subject is complete. This is a recipe for a lot of subs
Yeeees.
@david is a great video sommeliee
this is a sub at first vid kind of channel
@@zariumsheridan3488 an actually-turn-the-bell-on kinda channel.
Back in the 60’s variable names were limited to seven character. Additionally, by default variables starting with the letters I, j, k, l, m, and n were by default integers. We often used i, j, … as loop counters.
Hungarian notation was named after Charles Simonyi, a programmer who worked at Xerox PARC circa 1972-1981, and who later became Chief Architect at Microsoft.
I used to like Hungarian. it made reading the code so easy.
aka why implicit none exists now 💀
@@erato1look into "apps hungarian" vs "systems hungarian"
tldr: hungarian notation in its original form is good, but the windows API guys corrupted it
@@erato1 I still use it in C/C++. "pcwszLastName". Just by looking at it away from the declaration you now know it's a pointer to a const UTF-16 character string that's zero terminated. And it's buried 5 levels of nesting deep in a 200 line linear function with a single return at the bottom. With a comment block on top. Horrors!
It should also be noted that when Simonyi referred to 'types' he was *not* referring to variable types... he organized the code by the 'type' of thing that it was doing... drawing a window on the screen, capturing mouse movements, etc... the variables might be windowMenuBarHeight, windowMenuBarWidth, or mouseXCoordinate, mouseYCoordinate... the 'type' was essentially a tag that kept things organized semantically. Simonyi worked in the Microsoft applications division... when Hungarian Notation was adopted by the operating system group, the rule that 'variables should start with their type' got communicated, but the meaning of 'type' was lost.
One other rule I'd add: If you have two variables that contain similar or related data, give them names that tells me what's different about them. One maddening example I ran into was an order processing program that calculated shipping cost. And it had three variables named "frght", "freight", and "freightcost". I eventually figured out that one of them was the freight cost currently on an order that was being updated, another was a standard shipping cost taken from a table, and the third was a calculated shipping cost using a formula that only applied in some special situations. But wow, figuring that out took a lot of effort. If the original author had just called them, say, "currentFreightCost", "standardFreightCost", and "specialFreightCost" or some such, it would have saved me a lot of time.
Urgh, what a headache. People should not be allowed to write code like that.
I was just scrolling down to write the same thing! This is a huge trap that's a lot more common, and a lot harder to deal with, than anything mentioned in the video imo. So often in commercial codebases I'll see functions with variables like "fileList," "fileArr," and "fileTable," -- why do we need three copies of the same thing? What's different about them? Why is this one updated here, but not the other two? It boggles the mind and leads to a lot of wasted energy, especially when the original developer is long gone.
Better than f1, f2 and f3 :)
No no no no no. FreightCostCurrent, FreightCostStandard and FreightCostSpecial. Then they are all FreightSomethings.
One thing that helps is also commenting your code ._.
I don't understand why people are simply ignoring the easiest way of solving all those misunderstandments(even though, of course, naming variables good is the best and most efficient way).
I think the one exception to the "no single character variable names" rule is the naming of for-loop iterators. It's extremely common for such iterators to be named "i" and "j". If a developer looks at some code with an "int i;", they immediately know it's such an iterator.
I'll break this rule in Python if I'm iterating an iterator other than "range". "for item in cart" works well, but "for iteration in range(5)" really doesn't especially once you start nesting loops.
I used to agree with this BUT I almost always avoid for() loops in modern code. Usually when I am iterating across a collection, I want the iterated elements but not the actual index. And when I do want the index it is actually nice to have it called "index". Of course if you are still coding in C99 or earlier, then the pattern of using "int i;" makes a lot more sense
Another one i can think of is if you are expressing coordinates/position, then you could have like an x and y variable denoting the position along the x and y axes
i & j is a bad combination as they are visually easy to mix up, especially for people with dyslexia.
You also find another version of the problem with i & y or c & z. Both these pairs are a pain if you have to verbally talk about your code.
@@JoeSteele No you're right, there shouldn't be exceptions to the rule.
If you need the index your standard library has a function for it.
Pseudo code:
for item in collection {
...
int index = collection.indexOf(item)
print(index) // There ya go bud
}
Another major issue of one letter iterators is that sometimes you have multiple loops with references to different iterators at different scopes.
Things like working with multi-dimmenssionnal arrays for example.
You will need to write multiple loop with i, j, etc... and use those iterators at different "level" of iterations which just make the entire code confusing.
My absolute favorite example of naming variables in a program: The original programmer named all the integer variables i1, i2, i3, etc and all string variables s1, s2, s3, etc. And then apparently to "save memory" or something, he would re-use variables in different parts of the program for different things. After banging my head against the wall for hours trying to figure out how to make the changes I had to make, I decided to take a step back. I studied the program and figured out what each variable actually was, and did a search-and-replace to rename it to something sensible.
hahah you basically had to reverse engineer it exactly like people really reverse engineer decompiled code
so he did what the cpu does with registers. Only have 8 so reuse when one goes out of scope.
I've had to clean up code like this... took hours to figure out what so many tmp and num and a and b and c... tmp2 variables actually did
Reusing variables might just come from ASM times
Back then there weren't really things like names
You just had some adress in memory you wrote onto
And when the data at the adress fullfilled its purpose there'd be no reason to keep it occupied with no longer used data
And with how important micro-optimsation could appear there was a good reason to do so
But as soon as things hit anything that scoped variables there shouldn't possibly be any upside to reusing variables
That's the only sensible way to deal with such code. Rather put in some more work once than banging your head against a wall each and every time you have to get back to it.
Another nameing convention I've adopted is this: if I have a class that belongs to some "feature", like a "knife" belonging to a "watermelon" feature, I prepend the feature to all the classes that support that feature: "WatermelonKnife" - next, if this class has a reference to another class within the same feature, for example, "WatermelonKnifeSharpener", I will omit as many of the shared prefixes in the reference property name - to just "sharpener" in this highly contrived example. Then, my code might read something like "MyWatermelon.Knife.Sharpener...." instead of "MyWatermelon.WatermelonKnife.WatermelonKnifeSharpener..."
This is a good example!
I find GoLang extremely useful for this since it is a package oriented language. And you are obligated to specify the name of the package as a prefix, except if you are in your own package.
E.g.
Let’s say I have a domain ‘vehicle’ logic and I want to have a factory that makes vehicles.
In another package, the type would be represented as ‘vehicle.Factory’ and you would make it as ‘vehicle.NewFactory’ instead of ‘vehicle.NewVehicleFactory’.
I find this extremely useful since it’s built into the language and I don’t have to battle with my colleauges about good names.
Also, another tip for naming files:
If you have a ‘vehicle’ that’s an interface, that interface would go in ‘vehicle.go’ file. The implementations would each have their own separate file called ‘vehicle_car.go’, ‘vehicle_truck.go’, ‘vehicle_cached.go’.
This way, the interface and all the implementations are grouped together in a directory structure and not scattered across the directory, plus they are easy to search and access.
This sounds a lot like what namespaces are for.
. But I can instantiate a Watermelon object - if I have a Watermelon namespace, I end up with something goofy like Watermelons.Watermelon. I use namespaces and schemas in SQL for the brand of the whole application, or plugin.
Like FruitCity would be the name of my business, my namespace, and application. I would never instantiate FruitCity, that would be like my business's name. I might use objects from a System namespace, but I don't manage those, Microsoft does. And maybe I use a CuttingBoard plugin, and maybe it has a KnifeSharpener that is different than mine, but I can still use the plugin, because of namespaces.
Does it matter if it’s your watermelon?
I was on the no-single letter names train for a long time. I've since come around. Single letter variables can be incredibly useful as aliases when you need to repeatedly refer to the same item multiple times in the same line. The trick is the single letter name should be very short lived. It needs surrounding context so someone can infer what it is. Consider the Python code `new_columns = ['prefix.' + c for c in columns]` versus `new_columns = ['prefix.' + column for column in columns]`. I think the first one is much easier to read.
Thats true, i think it is an acceptable exception. I do this sometimes in very small scoped / short lived arrow functions or lambda expressions as well, like persons.filter(p => p.getAge() < 16), if the surrounding context is immediate or its a long chain of one liner stream operations. In such cases you dont need very long or descriptive names really as they may just lead to more breakds and prolonging the lines of code in the method/function you want to keep small (but readable). But in arrow functions which are a bit longer or its less clear i still use full names as i do in pretty much any other case. I was trained in my first professional environment during an apprenticeship by a real "oldschool" hardcore senior Java dev for 2 years, who was a very kind and brilliant man - i read many important books because of him and learned from him that no name is too long and i adopted this ever since (of course, there is such a thing as too long, but you get the gist). I only went to university after i spend years in the industry, and my fellow (younger) students were sometimes really confused why my code is so "long" (not vertically, but horizontally). Or why i split code in so many functions/methods/classes. Funnily enough, to them it made to code "less readable/understandable". But that is usually born from a lack of practice in both writing and reading code at that point still. They are not "used" to how code looks and reads other than throwing some stuff at the wall until something sticks and the compiler is mute enough (with suppressed warnings) to fulfill some early grade assignments. Not used to scopes larger than 1-2 files. I guess theres still a core of truth to staying "as brief as possible", which is subjective.
@@stylishskater92 There is the famous saying - "any fool can write a program a computer can understand, but only good programmers write code a human can understand" 😉
For the splitting of functions/methods/classes, I believe a sensible middle ground has to be found. Often, splitting is really useful for self-documenting code. On the other hand, I saw people replace "if (end < begin) { invertFunction() }" by "if (shouldInvertFunction(begin, end)) { invertFunction() }". The first one is clearly more concise AND it tells you right away what the condition actually is. Here extracting makes the code harder to understand.
Also, it obviously does not make sense to build a nice API with fluent builders, if people then extract the construction of an object to a method like "createObject(id, name, foo, bar, anotherProperty)"...
As for single letter vars, I fully agree. OK for lambdas, list comprehensions etc, maybe as a loop counter (if anyone still runs classic for loops...?) but otherwise, something to avoid.
Should probably be [f"prefix.{c}" for c in columns], but I see your point
Or "for (int i = 0; i < iterationsNeeded; i++)"
@@nvcbl Very true. Extracting a variable CAN make sense in this case to avoid re-evaluating every iteration, but please call it after what it contains, not after what it's for ;-)
The C# interface naming convention exists because C# code (including the .NET libraries) makes extensive use of interfaces, and often has classes that implement those interfaces but have the same name as the interface they implement. There's an argument to be made that this itself is an antipattern, but when you're dealing with just as many interfaces as you are classes, it really helps to be able to differentiate them at a glance, and to know what types you can and can't instantiate.
Tangential, but when I was first learning to code I didn't really get the interface pattern (or many OOP principles) and I very quickly learned that you couldn't use the "new" keyword on any type with a name that began with an "I" - this encouraged me to figure out what exactly an interface even was.
This. It is also the reason why if I write code for myself I actually use `A` for abstract classes. It's always annoying to try to instantiate those then find out "oh it's abstract, so I have to search which types even implement that"
In my opinion it's even good for readability `AStream` or `AWriter` or `ATruck` makes immediate sense.
there’s value in following the conventions of the eco system you work in.
There are rules for dotnet APIs, pascal case for externally visible identifiers, camel case for parameters, when to use Pascal case for abbreviations (at least 3 chars -> pascal case instead of all caps)
That makes it a lot less jarring reading and working with code from different libraries, yours just being one of them.
Arbitrarily adding something to public identifiers (like classes that get an A prefix) makes your code not just look weird. Imagine everyone would think they’re above those conventions.
Then everything would look different for no good reason, except different individual pet peeves of individual authors, but in the end most programs would look like a mess.
Because they are an aggregate of custom code but also using a lot of 3rd party libraries.
I for interfaces is mostly due to it inheriting a lot of stuff from COM, and lots of COM interfaces had the I prefix for decades.
Interfaces are different enough in semantics and performance characteristics (they force structs to be boxed when used as interface), that having the I is a very good compromise.
It’s also clear that you can implement them however you want, as opposed to abstract classes, that will always have some things wired in a way that cannot be overridden.
@@DarthJane we go even further, we call OUR enum with E (and abstract A too). Maybe this is bad, but this is our convention and this is helping us.
Indirection is an epidemic in OOP. While I personally stick to the style guide in C#, I do think that if you can't think of a descriptive name for your interface implementation that doesn't clash with its interface (without the "I"), then you probably don't need an interface.
@@DarthJane "It's A Truck" - hm, i like that actually
"You shouldn't name variables with a single letter" - *sweats in vectors*
Coordinate systems and loop variables are my exceptions to this rule. xyz, rgb, uv, hsl, ijk, etc. If you don't know what those mean then you probably shouldn't be messing with the code that deals with them.
Prefix them with vec
@milk guy I'd call hsl "hue/sat/lum". Short but also enough clarity.
Yeah, I take what was said in this video with a grain of salt. Sometimes, especially for variables with a short lifespan, abbreviating them makes the code a lot cleaner. Otherwise it’s just a mess with a bunch of variable names for simple operations
@@Hector-bj3ls also functional stuff
omg thank you for making this. Trying to learn from forums that use single letter abbreviations in their examples gets confusing so fast.
You can use abbreviations, even single-letter variables, just in the right context / scope. This is especially true with lambdas, which are often a single-line operation.
People who insist on "index" or "iterator" instead of just "i" are some of the most annoying types of people
@@NihongoWakannai I would love to see any remotely large computation written with full words. Ah yes,
easingParameter < 0.5 ? 4 * easingParameter * easingParameter * easingParameter : 1 - pow(-2 * easingParameter + 2, 3) / 2;
is sooo much more readable than
x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
Especially given the entire thing is pretty much always fully encapsulated.
It's just hardly anyone actually interacts with math at this level in code anymore. If all you're doing is implementing a textbook PDE computation or whatever, there's hardly any reason to invent new names.
Definitely, but even then I think you should ask yourself: is there useful information I could be conveying here?
It made a surprising difference for readability when I changed, e.g.
df.apply(lambda x: log(x.inc+1), axis=1)
to
df.apply(lambda row: log(row.inc+1), axis=1)
Or something like
for car_i, car in enumerate(cars):
...
My feeling is, x means a number, i means an iteration index which really doesn't represent anything, and most other things should be named.
I work a lot with code that models the real world, though. If I were writing like a crypto library where the vector is just a vector and it doesn't represent anything, I can imagine using single letters more.
@@mytuppenny On some level, a crypto library is less likely to use single letters because it deals with concrete implementations.
You may be writing an abstract library with lots of real world applications, but heavily grounded in math - that's where most of equation-like code comes from. Or consider physics sims. Between E, emf, and electromotive_force, emf would likely the best option to use in code.
writting lambdas is so nice. I looks very cool and is quick. But have you ever tried debugging a complex lambda you didn't wrote yourself?! :X
Back at university I shared my exercises with a friend who wasted an afternoon trying to figure out why I was multiplying something by 1.0. I spend the next morning trying to figure out my own code.
I was converting an integer to a float. That's what the 1.0 was doing there.
I will never forget the lesson I learned that day. Write in a way you will understand the code if you read it after 6 months. And comment everything without being overly explaining.
You can't comment everything without being over explaining - that's having your cake and eating it. Just use comments where you are doing something which is not that obvious, and a good way to do that is when rereading the code, if you spend a while thinking what does this do, then that's where a comment is needed. Less comments kind of make the ones that are important stand out. It's like councils putting safety signs on walls. Warning - these stairs are dangerous, make sure you are holding on tightly.
@@Andrew-rc3vh I once worked for a company where one person would write the code and another dev would add the comments. They'd just work through the code and any bits they didn't get after a moment's thought they'd add a comment. Pass it back to the original author to check they'd got the comments right (and rework the code where possible to make it clearer) then send it round for review. Worked pretty good. I tend to over comment my code while I'm working on it but I do a pass before sending it out for review where I strip most of that out - it was only for me as I was thinking through the code anyway.
@@xlerb2286 Well if you have a good naming convention the code itself becomes far more readable. I think the problem with the system you describe is probably the most important comments of all are the times where the code is unconventional and may rely on things some distance away from where you are commenting. It depends on how complicated the system is you are building, but without certain comments it can take hours to figure out. The other trap is if the comment says something different to how the system works, such as if the system was altered and the comment out of date. It's one reason I prefer to rely on reading the code than too many comments. Consistency is the number one thing which makes the code readable.
@ the brogrammers with the “good code speaks for itself” mentality: just learn to write a meaningful comment about what it does, I know it’s hard for you considering the advanced level of autism
@@Andrew-rc3vh Even then it’s usually easier to just build the logic into a tester and comment what you want to do.
Re units for variables: if your language has support for "newtypes" -- zero overhead wrappers for an existing type (like Haskell's newtypes, Rust's single-component structs or Python's typing.NewType) -- then consider using those. If you want a distance variable, then its type could be Millimeter, which is actually a u32. Then you can call it "distance: Millimeter" and not "distanceMillimeters: u32".
The newtype pattern is especially useful when you have a functions that accept multiple of the same type with different semantics. Like two different hash outputs that are both u32. It'd be easy to accidentally pass the wrong one. If you gave each a newtype then the compiler will stop you.
Just like you were saying with distance. Also applies to time and a whole bunch of other things.
Or if your language specifically allows denoting units for types, like F#
In D, you can define a public alias for your type, which can help to simplify logic a lot.
In my opinion it can make the problem worse. Now you have a new arbitrary type that you don't know what it is at a first glance. Is "distanceMillimeters" an integer or a floating-point type? Or maybe it's actually a new type with its own structure? Furthermore, this "type aliasing" can cause a type-hell where you start to do things like "intVector" instead of "vector" - this makes you question whether a function parameter is of a special type that contains some special logic.
While it's sometimes a nice idea, it's not something you should do all the time (maybe except using enums instead of booleans, although this is an actual new type, not just an alias).
Scala also has newtypes
"we have massive 4k screens"... we?
Not to mention that his font is tiny, which is not good for your eyes, and I often have multiple buffers side by side, with often a terminal thrown in, so having shorter columns can be good. I use pythons black's default of 88, which along with decent automated line-breaking (looking at you, gotfmt), works really well.
@@maleldil1same here, I appreciate thinner code especially since I use my laptop with quite a few panes open
These are all EUFA footfall fans
@@maleldil1 You can't know how "tiny" the font looks on his screen without knowing the size of his monitor and how far away from it he sits.
😂😂😂
Wishful
I tend to be wary of good practices that revolve around the words "always" or "never" because they reinforce dogmatism rather than an understanding of the conventions for coding.
For example, advising to never abbreviate (or use acronyms for) variable names may seem like a good suggestion, but the main motivation behind it is that abbreviations introduce ambiguity. Therefore, I would be more inclined to suggest avoiding abbreviating when it results in ambiguity rather than objecting to it as a rule. I don't think I'd enjoy reading through code that systematically turns "id" into "identifier" for this reason. Or one that expands a well-known abbreviation from the domain that domain experts routinely use in an abbreviated form.
On another note, I see a lot of value in making type names expose their abstract nature. Sure, from the point of view of client code, it doesn't really matter whether you're consuming an abstraction or a concrete type as far as API usage goes. However, this ceases to be the case when you need to start writing tests for your client code as concrete classes carry an overhead in that they need to be instantiated within tests. And within test environments they might be difficult to configure in such a way that the client code you're testing behaves in the expected way.
Now, you might argue that this merely reinforces the notion that we should aim to express dependencies through abstractions, but establishing a convention around identifying pure abstractions with an easily recognizable feature (such as the leading I in C# interfaces) makes it much easier to identify dependencies that can be easily mocked within tests. It also allows code reviews to more readily identify sources of strong coupling between client code and a particular implementation of a dependency. Lacking a clear identifier forces the reviewer to need to look up the dependency to make that assessment.
Agree with the third paragraph.
I personally have sometimes benefitted to some extent by using terms such as BaseService, although I use it sparingly.
I agree that there is value in making type names clear as to whether or not something is abstract or not but disagree with the Hungarian approach. This can lead to the laziness pointed out in the video of things like BaseTruck. Instead, things should be named based on how they're really abstracting something. Take these class names for example: Vehicle, MotorVehicle, Truck, TrailerTruck, or Animal, Pet, Dog, GermanShepherd. It's easy to identify is a class is concrete or not if the name of concrete classes are specific and the name of abstract classes are more general. The only thing this leaves out is if a class is an interface or an abstract class, but I would argue that this is less important than being able to understand a clear hierarchy as in my example and also that you're going to need to know more about the classes anyways in order to implement them regardless.
Yes, I use single letter or abbreviated variables all the time and it makes the code MORE readable. Any time I see people spreading dogma about coding style like this it makes me think they don't actually program and just regurgitate what they heard online.
From your first points, I disagree in that I think `ALWAYS`, `NEVER`, `MAY`, and `SHOULD` are all great, super important words for hard rules. You're looking at these rules in the video and I think the main problem is that the rules in the video are just plain naive and badly thought out. Well crowd analyzed ideas that include these words can be excellent, and a rule defined this way doesn't prevent being given context to understand it
my man, why in the goddamn world would you want to save characters?
For Python you can also use type hinting as a middle ground where the variable name doesn’t have the type and you get the clarity you’d have with explicit types.
Python also has the datetime.timedelta class. It's a little weirder than the equivalents in some other languages, but it works well enough.
I've been using type annotations in Python 3.8. In 3.8 they're kinda clunky (i.e. forward refs were added in Python 3.9).. they're still very useful. They help readability. Help you plan out functions (i.e. "I want to pass in these types of arguments and get these kinds). And they help Pycharm provide do type-checking, auto-complete, and find usages of a function or class.
@@jocassid1 if you feel like regular type annotations are not enough, you can do "import typing" for mkre such as Iterator, Generator etc. Pydantic is also a way to make your code simultaneously more and less readable at the same time
@@khuntasaurus88 "Pydantic is also a way to make your code simultaneously more and less readable at the same time" - so:
import pydantic as quantum_pydantic
;-)
I think the reason why the IThing naming pattern is standard in c# is because you almost always want to deal with interfaces and not classes directly. Which means you always have to come up with two names. One for your class and one for the classes interface. Having a standard way of naming interfaces is, therefore, very convenient. It would also be incredibly confusing if you opened up a project where the interfaces had different names than their implementations even if where they only had a single implementation each.
Not that I use either but I think I'd prefer the way that it's done in Java (albeit less frequently, Java traditionally hasn't been as focused on interfaces as C#). Instead of IThing and Thing you'd have Thing and ThingImpl. It makes the interface you should use anyway have the "main" name and the default implementation comes alphabetically directly after the interface, making their connection obvious in most listings, be it of types or files.
@@swapode
I think using Impl in the name of type makes it a bit of ugly, but I do respect your point.
But, in defense of C#, prefixing interfaces with 'I' makes them easier to identify in code and even using intellisense, as we have both classes and structs in C# and having something more to differentiate interfaces turns the differentiation to be easier. It also helps with the naming of things a bit, as we can think on interfaces as they where simple units of contracts for specific things (or even traits nowadays), like "IDriveable" (it reads like, I driveable or I am driveable, you could also write IAmDriveable) or "ICanFly" or "ICachedDao", for the most part it's actually interesting.
So instead of having interface Car and CarImpl, you can actually have the interfaces IHaveRoads, IDriveable, IGasBased, etc... and have a concrete type Car implementing these.
It's there because C# is basically MS Java. And Java had it first.
@@tabularasa0606
At this point C# is much more than Java ever was.
ThingImpl is a disaster. It implies that you will only ever have one class that implements Thing. It tells me that you have have been told to use interfaces everywhere and you’ve applied the rule without any thought.
The c# convention sucks even worse. It has the same problem but the additional “feature” that everything in your public API starts with an I. That’s not great for readability.
Base, I-prefix, etc are handy in cases where as a user you may need to handle both the base class and the derived/concrete classes at different points. It's important to remember your API communicates with other programmers not just in the context of a single function, but in the context of potentially many different problems they are trying to solve. Understanding that something is a base or even has a base can help the programmer understand the API easier and know how to use it to solve later problems. e.g. I'm programming something that doesn't care about the given truck so it takes in BaseTruck, but that signals to me that there are several different kinds of Trucks potentially, so if my code needs specifically a DumpTruck I may be able to find it. It also tells me that I might be able to inherit from BaseTruck and make my own truck if the library doesn't provide 100% of the functionality I need.
Correct, he is absolutely wrong on this. Good, well written libraries that are intended to be extensible do in fact do this. His perspective tells me he has little experience or is not good at writing reusable code and libraries. Its a skill sadly most developers lack.
The abstract class will be intended to be maximally flexible and allow developers to create descendants with new functionality. But such classes are often combersome for common use cases. So there is a default inplementation that is easier for programmers to use for typical cases. Thus something like VehicleAbs, TruckAbs and Truck as classes. His example of Truck vs TrailerTruck makes the case that he does not fully grock inheritance and good practices for reusable code because a trailer truck is NOT the same as a "truck".
Also the use of I is quite handy. Often there is an interface and a class that implements it which share a name. The inclusion of "I" lets you tell the difference. Also makes it obvious when a junior or mid developer is usine the class type where they should be using the interface. One pattern I sometimes use is that the class is mutable for use in core code but the interface only provides read-only access. In which case having the interface named with an "I" is quite helpful.
Also abbreviations can be fine if relatively standard and not abused. For example "Abs" for abstract if its used consistently does not at all harm readibility but can shorten some very long names.
I think there are often good reasons to keep names short, even single characters at times. This serves to show that the thing is of little importance and short-lived.
For example consider this:
button.onClick = (e: Event) => this.buttonClicked(e)
In this case you know that e is an event, it is already given by its type and by knowing that you only assign event handlers to "onX" properties. The variable is also used immediately on the same line and never again. Naming it "event" or "clickEvent" or "click" in this case doesn't make your code any easier to read, but rather makes it bloated and takes your attention away from the important parts, which is the assignment and handler.
for (int primaryClassArrayIterator = 0; primaryClassArrayIterator < cArr.length; primaryClassArrayIterator++) {
for (int secondaryClassArrayMemberIterator = 0; secondaryClassArrayMemberIterator < cArr[primaryClassArrayIterator].getArray().length; secondaryClassArrayMemberIterator++) {
you missed the point here
Using the words "always" and "never" invites lots of "what about"s. I think the guideline (not rule) should be "Favor readability over terseness". There are obviously some things so common no one would get confused; like using i for a loop iterator, using x and y for 2D coordinates, etc. I would say in the latter case, Player.pos(x,y) is much more readable than Player.screen_position(screen_x_coordinate, screen_y_coordinate).
@@BrianMelancon whats why you should never use "always" and "never". I say that sentence often and yes, the using of the word never is to underline the point :-D
Yes! There's a small amount of conventions like "e" or "evt" for eventhandlers and "e" or "ex" for exceptionhandlers
I'm a big fan of verbNoun naming on functions, it is a great way to say what the function does, and in many cases, clues you in on what i might return in a preloading sort of way. Not just in terms of raw types, but in terms of the meaning of what it's trying to do. so things like a getMovie() function, or a getMovies() function, tells me I'm going to get back single objects, or likely an array.
Another is to always name arrays and their ilk with a plural, and the singular instance with... well a singular.
These aren't ground breaking by any means, but they go a long way to helping you write code that is easy to mentally parse with common structures, like for..of loops.
That plural versus singular idea gets really problematic really quickly, because if you had an array of Things and just one Thing , you’re only one letter off from an autocomplete or typo messing up your day.
If one of something is getting called Thing, you’d be better off using another longer word to append after it.
Back in the day they always liked putting the type or whatever abbreviated in front as mentioned in the video, because that’s how they taught us to write code in a VB Class over 20 years ago. I prefer flipping that around so I can easily see the slightly more thing first, followed by the descriptor. So, for an array of more than one Thing, I would go with Thing_Array or Thing_List, that way if I go searching for “Thing” I can see where I used both forms of it, just in case I forgot what the prefix I came up with was.
Sure, I could put a wildcard before the word I’m looking for, but I like putting them afterwards because that’s how I’m built.
@@kernelpickle
You're entitled to your methods but not a compelling argument imo.
messing up your day being like messing up 30 seconds max since even just a linter should catch this, and this never happens to me despite doing this all the time.
It would take longer to fix in dynamic languages, but even python has type hints.
Autocomplete requires your input, if you need an array, you're already looking for plural, if not, you're already looking for singular. The autocomplete should not have to do your thinking for you.
And definitely not a fan of the postfix hungarian notation alike.
Document your function in the documentation, not in the name.
@@UnknownGamer40464 yeah, you're never gonna confuse a single object with an array, they're not interchangeable in code, your IDE will yell at you immediately
Function_Maker_Factory.make_function_maker(function_maker_factory, function_maker_params_dict)
One problem with this is that some words don't have a singular and plural version. For this reason, I will name my functions getMovie() and getMovieList().
It happened to me multiple times that my code felt difficult to reason about, and then finding the right name for a class made everything much more clear and simple. Going out for a walk sometimes helps finding the name 😀.
I'm guilty of having a "Utils" class quite often, but mostly it's for when I have a bunch of fairly generic functions that I don't want to have 7 different classes each with a single function... Although I'm now moving towards having those classes, but putting them in a directory called "Utils" which is tidier. Speaking of which, I'd love to have a video on structuring projects in terms of the file system. I.e. what directories do you have, how best to split up different classes, that sort of thing.
agreed - sure, it's a clear sign you should ask yourself some questions
'is there a better place right now?' and 'how unwieldy/disorganized is this utils file?'..
But also: 'am I letting perfect be the enemy of the good enough?' ... but in my experience, the answer is context dependent. Many times the best organization is something that reveals itself over time and overthinking/overworking it now ends up being a waste.
I often start with a utils class but usually end up refactoring its contents into dedicated classes that make more sense
Training (UA-camrs teaching) typically rely on contrived and overly simplistic examples because ugly real world examples often require compromise. Few really experienced developers would want to deal with the comments section of social media.
I never have a Utils class, but I do often have a utils file that contains common, reused utility functions.
"Moving standalone functions into classes" isn't a very good advice... Or at least, it's a contraversal advice, because it directly contradicts with official C++ Core Guidelines by Bjarne Stroustrup and Herb Sutter, section C4: "Make a function a member only if it needs direct access to the representation of a class".
A lot of times I see suggestions on the internet about clean code and coding standards that doesn't take into conderation any other existing documents and practices.
I think "never abbreviate" is a good bit of advice for someone addicted to it, but for some code it's fine. I'll name something x if it makes sense to, because I don't wanna write a 5th order polynomial where ever x is "firstOperand". This is the first thing everyone thinks with array languages, but terse notation is good. It's also important to make sure the user has the context for it first, and to justify the learning curve. Sometimes you can (I love array languages and maths-y code), but mostly you can't.
Also if I’m doing something like a lil for loop I’d rather use i for convenience
To me i is a standard way to specify a simple index variable.
also, code comments are like, a thing, so if you want to keep the variable names short for convenience you can always just describe them in the comments, and there's no point having the names long if you ALREADY have to describe them in comments anyway
It's not about abbreviation, it's about context. If your context gives you enough information already, then you can abbreviate to xyz because you know what it's about. But if you are missing the context around it, then you should not abbreviate because this is obfuscating the code. The rule could be called: "never abbreviate context" rather than just "never abbreviate".
@@ayaderg i think this is a really bad practise. Dont choose bad names and describe them in comments. Choose good names and dont comment.
Wow, this is like 3Blue1Brown teaching coding practices. You’re an awesome teacher 🙏
The super class having 'Base' in the name actually hurts a lot in the code I work on at work. We're in the kitchens industry and we have a thing called a base unit (or base cabinet in the US). So if you see a class 'BaseUnit' is that the base class of 'Unit' or is it the class representing a base unit? Well, the earlier devs didn't realise this so we have a bunch of confusingly named classes.
Reminds me of working with Joomla CMS. The convention there is to name ORM model/ActiveRecord classes "Table". Meanwhile, the application domain was publishing web content in HTML tables. Hence the not at all confusing name "TableTables" 🙃
pain
Let's not forget about HTMLHtmlElement.
This is so sad
@Ian Gregory this reminds me of some legacy databases I've sometimes work with. We have tables prefixed with the name of the database. For example, cms.cms_user, cms.cms_permission, etc. Makes me cringe whenever I come across those and I seriously want to write a migration to rename all those. What's worse is seeing column names following the same pattern where they're prefixed with the table's name, like user_firstname, user_lastname, user_username. AHHHHH! CRINGE!
I agree with most of it, but I find typed types pretty useful.
I believe the "I" prefix for interfaces is quite helpful in understanding how the code is structured. For instance, I don't have to go to the source of a type to know it's being used for dependency injection when it's prefixed with "I" and within a certain context. I also know that to actually find what the calls to it do, I'll have to find implementations, not its own source. That goes a long way when navigating code I didn't write myself.
In Unreal Engine, for example, you have T for template types (like TArray), F for structs and other prefixes that are helpful to know what usages, limitations and capabilities you can assume from those types without having to read their full source.
Not to mention distinguishing things at a glance. E for Enums, for instance, so when I see EName::Value in C++, I know Value is not a static property of some class, it's an enum value.
This! 💯
Also if you press "I" your IDE will suggest you every interface relevant to that context
I remember reading that Hungarian Notation actually came from one of the early programming leads for MS Word. The initial character actually had nothing to do with the type of the variable, it was meant to specify the type/units of the data. Was this a screen-space pixel? Maybe you have s_Width and s_Height. But for where you are on the document, you'd be measuring things in inches, so maybe i_Width (for inches) or d_Width (for document units). It was meant to inform the programmer about potential conversion issues that a simple 'int' wouldn't tell you.
The other limitations of the time probably played a big part in them using small prefixes instead of writing out the whole words (or creating new types, like we could now). The real problems with Hungarian Notation came from other departments attempting to mimic the Word team, but not using (or knowing of?) the team's naming guidelines.
I don't use Hungarian Notation myself, but I think it gets a lot of derision without knowing the original intent of the convention.
The very next recommendation after 'don't put types in variable names' was 'do put units in variable names'. Which, as you said, was the original intention of Hungarian notation. It's an interesting coincidence.
This. The original Apps Hungarian guidelines also specified to use "from" naming rather than "to" naming for conversation functions. For example, to convert from screen space to inches you might write:
i_width = i_from_s(s_width);
This made it easier to visually identify type mismatches because the prefixes on the left and right should always match.
Never heard of the term Hungarian Notation before. I prefix types into variable names simply for better readability of the code. It can be useful if I hadn't worked on a project for months and I have to look into it for some reason.
So now we can deride it while knowing its original intent ;-)
"the early programming leads for MS Word."
Charles Simonyi. He was the lead for developing office (and is quite wealthy now got his trouble). He also has a PhD in computer science, so he's not just a business flak.
I've read (and poked around in) C++ code that was a large ten effort that used Hungarian notation. It was pretty helpful, actually. Just because the type is declared *somewhere* didn't mean it's not way easier to determine it where you're looking in the code.
Wise words! I think the most important word in all of this is "guidelines". Yes, most of this makes sense most of the time, but there are times when deviation makes more sense (x,y,z coordinates etc). Another important concept is consistency - I hate to see numMovies and movieCount used in the same codebase.
Well it could get confusing if the programmer, for some assmad reason, had decided to use y for the 1st. horisontal axis, z for the 2nd. horizontal axis and x for the vertical axis.
I guess one could technically name them horizontalX, horizontalY and verticalX or something to ensure less ambiguity even in contexts with "implicit" conventions like in coordinate systems.
I definetly use only movieCount. Num or number is an ambitious word for amount of something (and 'amount' itself, too).
Well said. People treating guidelines as some kind of immutable law makes code worse, not better.
@@kristianjensen5877 this does piss me off, in minecraft, your coordinates are x and z for where you are in the world, and then y is height. it annoys me, but i got used to it. i'll never do that in my code because no
In an oddly specific case, if you are using Applesoft BASIC on an ancient machine. You have to abbreviate your variables to 2 characters because the language differentiates and refers to the variables via their first 2 characters.
It's obviously a general rule and you will recognize when you can't do that for one reason or another.
Commodore basic is the same way. I think they're both based on microsoft basic? so maybe a microsoft basic quirk. Also I think Ti calculator BASIC is limited to single character variables? At least on the old z80 ones?
Programmer: **needs 677 different variables**
Apple: skill issue
That's a bit like criticising driving schools for advocating the use of indicators (turn signals) because a Model T Ford doesn't have them...
@@craftsmanwoodturner They were making an interesting trivia comment, rather than criticising the video.
When I started programming in 1978, BASIC supported two character names and C supported eight character names. That's why we used to abbreviate. It never had anything to do with screen width. But that's okay, the guidelines in this video are right on.
1. Abbreviations are fine if they're readable enough and widely understood. If it's a "everyone in the industry knows, but it might be hard for a beginner"-cases, I like to use docs (like the XML annotation in C#) to spell out the full name.
Even though it's true that screensize has expanded, there is still a point at which length makes names annoying to read.
2. Totally agreed with units! Use a type of you can, a variable name, or at the very least add documentation. Don't make me guess!
3. Utils are contextual. In smaller projects, I like to keep a fairly general Util class with generic stuff like float equality checks with epsilon. Never had a problem with those.
In bigger projects it's sensible to look for a more formalised approach... but the last 20 million line project I was working with still had a simple static Util class and that was one of the few things which just worked while everything else was a mess ¯\_(ツ)_/¯
2. i enforce this rule in my company. and additionally, if a variable or function returns a specific data type, especially if it returns boolean, state the variable/function name in a way the boolean would answer it like a yes/no question. E.g. instead of "checkAuth", write "canLogin" or "hasAccess"; if a function checks whether all systems are online, instead of "allSystemsOnline", say "areAllSystemsOnline" because "allSystemOnline" could be performing an action that makes all system online, but if a function is called "areAllSystemsOnline", then it is obvious that function is only going to perform a check rather than actually trying to bring all systems online.
Also, please please please, bar some really rare scenarios, don't use negative words in names. !$user.inactive is just that much harder to read than $user.active. we are dealing with enough logical challenges already, no need to add double negative into the mix, ain't nobody got time for that.
1. True. In some cases single char vars are ubiquitous. Like the "i" in loops. Or short names for "exception" in a try-catch. But in custom cases like "durationSeconds" I'd strongly prefer using that name as the var name - as opposed to a short name and a documentation/comment. As less docs/comments is just...less source and fewer stale things that can become outdated
@@BenRangel yeah. if it's milliseconds though, then "durationMs" is fine for me, because "ms" is a very well established abbreviation (as long as it's obvious that it refers to time context / duration).
relying on extensive documentation is fine if you're writing an API or a library.
out of my experience, comments generally aren't read (even when they should be) - developers seem to develop some sort of a "comment blindness", probably because they're so often outdated or incomprehensible to begin with, so people kinda give up on them by default.
i'm doing my best to avoid having to write any comments. if i find myself in a situation when i'm tempted to write one, more often than not it means the code needs to be rewritten. you need to explain what those 10 lines accomplish? extract those 10 lines into a well-named function; make that name take care of it.
cases where comments are unavoidable happen sometimes - like a complex workaround required by a quirky bug in a third party library - but they're few and far between.
@@vibovitold Agree with everything. All devs are familiar with "ms" ✅ and docs are worth if it's used by lots of people, especially public apis ✅
I think it's fine to comment methods in a format that shows up as hints in editors. But wouldn't do it in my own teams codebase - I figure most of the time if the method name and args are simple and clear we don't need that comment. I don't hate them though. But I mostly use comments as a rarity to make people look carefully.
I mostly comment weird stuff. Such as business decisions that seem wrong. Like "this format is obscure but we need it for marketing"
(used to rely on git history for that type of stuff but after migrating between repos and file structures that kind of history can become obfuscated)
In my company we use Utils for misc functions and extensions which are used in mutliple projects. Something like calculating a hash for some data which just doesn't warrants making a complete single funtion class out of.
And some Extensions for EnitityFramework like AddIfNotExists or stuff like that.
Thanks for making this video. I've seen a lot of videos that churn on for 15 minutes about a single, overengineered design pattern for "improving" your code, but these tips are super useful because I've personally suffered from a few of these mistakes quite a bit.
Verbs in function names are vital. For my projects, get should always refer to getting something from memory, which should not be confused with "pull" which might contain a get or a post request, "load", "write", "serialize"which contains io, or "calc" which contains some sort of calculation. This is key when it comes to something like a double for loop inside an update method. Saves hours during debugging phase.
get is commonly used for code that does a GET request
@Jabberwockybird maybe for some, but never for my projects
wouldn't "request" as a verb make more sense than "pull"?
@@fiona9891 depends, there is also a subtle difference there too: 'reqXxx()' implies the remote host may potentially reject the request, whereas 'pullXxx()' implies that the remote host only servers to provide information.
@@fiona9891 the one I've seen and prefer is "fetch"
its beautiful, it reminds me that sometimes coding isn't always about producing but also about consuming. we hand our codes from one to another until it reaches the user.
This is the channel that was missing from my life. You can learn algorithms and practice and learn advanced techniques but it's like no one sits down to help you learn these kinds of things. Thank you so much, subbed.
I think an exception to the Utils naming is when the natural owner is a sealed class. For example in Java you might have a utility to get next midnight after a date in a time zone: public Date nextMidnight(Date after, TimeZone timezone). Ideally is you had extensions (like swift) you could just add it to the Date class - however in Java you can’t and are forced into DateUtils static methods.
Yeah this is fine imo
I think the real problem is with names that are essentially just euphamisms for "miscellaneous" as in they have no qualifiers of what they relate to at all, like "utils", "shared", "common", "globals", "constants"
Often these things contain a bunch of random methods or values, that could actually be organised but just aren't because the thought didnt occur or a good name was just not happening
@@licriss and let's not forget the immortal "SomethingManager"
I was going to say the same thing. If you're using a 3rd party library and cannot simply add new methods to existing types, this can be useful. In C++, I would go with a utils namespace, or perhaps a {module}_utils namespace, rather than a class.
You could build a class to extended it. A decorator of sorts. You might want to avoid creating unnecessary objects, tho.
@@LeoFuso Yeah, I wouldn't create a class if I don't need state.
I found it helpful to have prefixes to methods, getters and such that return booleans. For example: "active" -> "isActive". "needsUpdate", "isRoot", "hasTag", etc. Not a rule, but a guideline. Makes it easy to know exactly what you're asking for and what you're getting at a glance.
The naming convention of VerbNoun is a _very_ handy one.
This is pretty much the same as Hungarian notation, just a little better.
Yeah this for booleans is one of my favourite naming conventions
@@MichaelPohoreski Yes, but when the underlying language is English then I found the "verb" part (too) often be indistinguishable from a noun. English just has too many words that are identical as noun and verb: capture, run, start, stop, display, screen, filter, tag, mark, file, store, jump, find, loop, click, push, in-/decrement, in-/decrease, change, set, update, call, signal, message, ....
Ruby allows for question marks in method names so you can replace (for example) 'isValid' functions with 'valid?'. Really enforces the idea that you're asking a question and getting a boolean answer.
This is almost exactly the beginning of every discussion I had with one of my devs when we’d talk about writing better code. Having just retired from software development, it was a nice trip down memory lane!
The next part of the discussion was always about managing (mostly by minimizing) dependencies in your code. This was more important than naming, but you always had to solve the naming problem first!
This might be the best channel on UA-cam. It hits that perfectionist urge and answers questions I didn't know I had about my code style. THANK YOU for the slick editing and soothing music. I actually pay attention and learn stuff!
Well, I wouldn't go that far, but Herr Jürgen* does agree with most of what is said here. *A mythical figure, a twisted meticulous nemesis hunting down bad coders.
I 100% agree with you, Olin. I would, indeed, go that far.
I think utils is fine for extremely general purpose functions that are used in many places in the code
I think it’s also useful for creating helper methods for working with external libraries
But idk I’m still a junior so maybe there’s a better way
It can be, but is definitely an overused name just like "Shared" and "Common"
All work sometimes but if a more specific name can be given then that often makes code easier to read, packages easier to navigate and when you forget the name of the method but kept the class name simple gives you a much shorter list of methods to scan through when you're looking for it
also good for unit testable methods without having to expose all the methods in a class that would otherwise contain these methods
Agree but i would add a suffix or a prefix to specify what kind of utils they are, to avoid multiple utils having same names.
What I would add is that good naming should first prioritize function names and arguments! The body of a function matters when you're working within that function, but the name of the function matters everywhere it's used. And when you are looking at the body of a function, the things it calls are some of the single biggest clues about what it's doing!
2:00 Who's we?
proud 1366x768 and 80x24 terminal user
@@cirkulxYou're proud of being outdated and less efficient?
Lol, I'm watching this video in 144p, so when he asked us to see the difference between the two codes, I was just like "Yup, this is code. Idk what's written, but I bet it's good"
The most important thing is consistency. If you have a variable for the amount of cookies you have, there are so many ways to name it.
Cookies
cookie
amtCookies
Cookie_Amount
Come up with a naming system that works with you, and it will avoid so many issues down the line.
" int nbOfCookies; " is the one that i use
n_cookies has become my standard form since doing a neural net book. It's clear and concise. What more could you need? :)
int cookiesNum;
I prefer nCookies
I would use cookieCount myself. If I saw "cookies" in code I'd assume it was the actual cookies, not a numeric.
Base and I prefixes in C# in my experience can be useful. Of course, they're easier to find because you can filter out derived classes and implementations, but they also indicate that whatever you're currently using is definitely something else, because you can't make an instance of an interface or an abstract class which a base class ought to be. It helps keep track of abstractions.
Maybe I'm biased coming from C#, but I find the interface prefix I-, and the base class previous Base- useful in understanding the contract I'm making with consumers of my code. Especially in TypeScript, there's a noticeable difference between saying "I expect an interface that implements this method" vs "I expect an abstract base class that defines these members" vs "I expect a concrete class of this type, or a subclass". This is the difference between passing curly-braces with a named function, a custom class that extends a base, or the need to use the provided types
I wonder what is video-maker's job. It all sounds great when we're talking about real objects, but when you get into more abstract object and business logic, it's not as obvious if you're expecting the user to implement an interface or just reuse some explicit class.
I work in a space where the "I" or "Base" is very useful.
I believe according to Liskov substitution principle, it shouldn't really matter if the declared type of the received object is abstract or not - you'll receive an object matching the type's contract. I assume it matters while writing low-level, performance-oriented code, because then abstracting away implementation details is no longer beneficial. That said, C# is a high-level language, where I don't think it matters that much. From what I see, ITruck is not better in any way than just Truck, for they define the very same contract, the contract for the functionalities of a truck. Hence, I don't see how these prefixes help in any way to organize abstractions, for abstractions are all about their contracts, not about through which otherwise transparent language feature they force those contracts. Knowing what contract apply to the creation of the argument objects in my opinion is not the responsibility of the function. The function should perform its action on the object and call it a day. The contracts of how and where such objects can be created should be defined within the type itself and only be cared about during implementation of said class. Plus, in many IDE's, as in VS, interface names are even highlighted with a different color.
All that said, I guess most people just got used to do things in a certain way, so it just feels right to them. When I wrote C# I did follow their naming conventions. After that, I carried that naming to my private Java projects, but there it felt like clutter and I reverted back to Truck instead of ITruck and never miss them ever since.
Yes, I and Base prefixes definitely helped while coding. Way better than those Java implementation classes adding Impl suffixes. And I still remember that I was dead trying to create an instance of List in Java. C#'s I prefixes just makes it so much clearer.
@@scriber36 While you are mostly correct in terms of contract handling by machines, humans don't think of OO code in terms of contract. They think of OO code in terms of types. Knowing this, providing as much useful information about what you're currently using as practical is great for making maintainable code.
LSP and other SOLID principles make code functional and robust, proper naming makes it readable.
As for syntax highlighing: there is way too much of it to just tell what it is at a glance. Color coding needs to be, well, decoded. An I in front of an interface tells you immediately and definitively that it's an interface.
While I appreciate you sharing your experience of not needing them in your code, I am currently managing a 20,000+ lines project spread across 12+ microservices with up to 5 levels of abstraction of dozens of interfaces and base classes and hundreds of derived classes. I found proper, informative, distinct and consistent naming paramount in managing something like that.
Also, while making a base class abstract isn't strictly necessary as per SOLID principles, it is very useful to signify that you must use a derived class if using the base class makes no sense, which is often the case in a complex hierarchy of multiple implementations.
I think the I-prefix can still make sense for interfaces when they appear as file names. It's arguable for sure, but I think having the name in the filename makes a big difference on the impact of the prefix.
Yup, and when you glance at the code you know its an interface and can plan accordingly. Its more readable.
If i worked with a library or framework specialized for apple or apple products, I would be disappointed if there werent i-prefixes everywhere.
We use exactly that - "I" prefix if it is an interface-class, "_t" suffix if it is a plain data-class, and very few other rules on naming stuff. It does make it easier.
I believe that the I-prefix is just a variation on the Base- or Abstract- thing. Like, a WeatherService and IWeatherService could be better named as GoogleWeatherService and WeatherService.
I would keep the I-prefix in languages where the standard library uses that naming scheme, but still apply the more-specific class name (i.e. in that case they would be GoogleWeatherService and IWeatherService).
bep
C is and was a statically typed language always. 2:35 It was not the language, but rather the lack of IDEs that give you all extra information of the variables at hand. The type as meta information was encoded in the name.
interfaces are prefixed because you usually have a concrete impl of the same name, also because of var it doesn't matter that it has a prefix - the interface is mentioned in like two places only
Yeah but his other point kinda covers that. You have an interface with the same name as another type, you should rename one of them. I used to think a little bit of hungarian notation was fine (I learned to program in game maker so half the assets in my game started with spr_ and obj_) but after learning a bit of Haskell and Rust (where the type straight up won't compile if it's not PascalCase, and variables won't compile without camelCase/snake_case respectively) I realized how unnecessary it is to ever use hungarian notation.
It's probably about convention or maybe something inherent in a programming language I don't know but if there is only one planned implementation for the interface (which is implied by the interface and the implementing class having the same name) then why bother with the interface at all?
@@fletchcanny717 Renaming them just for the sake would be stupid, you would be forced to come up with artificial names all the time. I'm so glad we're prefixing interfaces in C#.
@@lahodal if you're struggling to come up with good names for your interfaces thats a good indicator that it's a bad abstraction. So many problems are made easier without the complexity of polymorphism.
@@fletchcanny717implementation (interfaces) and inheritance (class : class) are not the same thing. his point of TrailerTruck : Truck makes sense because it's inheritance. interfaces aren't "base classes", they're literally not even classes, they're blueprints. you'd then have TrailerTruck : ITruck which is the same as Truck it but makes it super clear that ITruck is an abstract "contract" that might be a class, but doesn't necessarily need to be.
these standards and guidelines exist for a reason
I use vim so the width occupied by the code still matters to me 😂
On a serious note though, sometimes variable names can be too long to be considered "more readable." My rule of thumb instead is: if that identifier still makes sense in the context of that block, then I'll use the least amount of words as possible.
Use context, no need to be overly verbose.
Why does this video so informative yet so calming at the same time. Good job!
As a firmware developer, prefixing variable names with datatypes is very useful. When I see a variable s16Speed, I immediately know that the variable can hold a max value of 32767 and a min of -32768 and having that info in the back of my mind can save a lot of time while debugging.
I started doing firmware in '84 in assembler and then C. I agree with you on that front.
I've always been a big proponent of having a written coding convention/standard that can be passed along to new hires (others) that come into a project.
I've seen WAY too much code where this doesn't exist, and 'the next guy' comes along and just does whatever they feel like doing. The result is never good.
From my experience translating programs from one language to another or reviewing old code that I remember almost nothing about, it helps a lot to add the type. And also use types that specify the size, like Int16, Int32, etc.
Why wouldn't it already be prefixed as ushort? Is it not C?
I'm an embedded engineer too and I absolutely loathe Hungarian notation. I've never found it useful, and what I have seen is type changes made without updating the var names. But when you DO update the var names, then suddenly your code commits have noise in them, likely obscuring actual changes. Never used it, never have regretted it.
@@marccygnus I haven't worked with C/C++ etc. in years. We never used type in variable names. We also never wrote functions/methods that were so long that one would get lost in a mess of code and not be able to see the declarations.
I gave up on writing comments except for those special outlier cases or for API/interfaces. I can't tell you how many bazillion lines of code/comments I've run into that didn't match due to changes.
I actually do abbreviate names in certain rare instances of writing shaders. I write water system using Gerstner waves. I link the formula I use as a comment and then I use exact same names as math counterpart. I find it really helpful for complex math.
Yeah that checks out. It seems that everyone that writes shader code is afraid to use a variable name more than 3 letters long xD. Makes reading shaders you didn't write a pain in the ass.
(but yeah for math stuff like you mentioned it is very helpful to make the math and variable names line up, so that gets a pass)
@@lunafoxfire having the maths as the comment is pretty great too
I think abbreviation has advantages. Smaller names occupy less visual space on the screen, making it cleaner. Very big names makes the code harder to read, in my opinion. Some abbrevations are completely fine, like "std" for "standard", which is commonly used. Also, in the example following, you use "NumUsers", whici is another good abbreviation.
I agree. Not using abbreviation is only good for relatively simple class, but not when you need to get data from member of member of member of an object, and all of them have long names.
Also not everyone has 4k monitor. I'm still working on my 1080p laptop and a sub 1366x768 monitor.
Eh, it makes sense to abbreviate if the abbreviation is more canonical (like "i" in for loops) but gone are the days of 80 column displays. We have the aspect ratio to display slightly more verbose names, and I'd much rather read code which has too descriptive variable names than too short names
@@gazehound Screens have gotten bigger, but our brains haven't.
Abbreviations are mostly bad. Except in such cases as the abbreviation is so well known that it's effectively a proper noun. It should be common enough that even if the reader does not know it, they can pop it into google and the first result is the definition.
I would definitely just call it "Users" with an integer type. Unless I also want to also work with an array of users, in which case it would be preferable to do len(Users) instead of having it as a separate variable.
I think abbreviation is fine, but don't over-abbreviate
Like, "std" and "NumUsers" are understandable, but "s" and "NU" aren't
It's been around 20 years since Martin Fowler wrote Refactoring Code, and most developers are still writing unreadable code. Thanks for sharing this 🙌
Great book. But kinda feel like every dev has to go through a journey to become interested in best practices of refactoring. And some never get there. They're fine with "it works".
And some that learned it in once might forget bits of it over time as they get stressed over learning new tech 😔
`for (int iteration_index = 0; iteration_index
I think the single letter variable names came from a combination of low memory machines and 80 column wide monitors.
Also, some exceptions to the no single letter name rules are: i, j, and k in loops and things like x, y, z; u, v; or r, g, b; etc... Any of those axis labels in coordinate systems.
You are right about the origin of the single letter variables and that's the same reason hungarian notation was around, as an helper, for old programmers the letters aren't gibberish, for newer programmers they definitely sound like that. 'strCwd' sounds like a seizure but an old programmer will recognize that it's a string representing the Current Working Directory. We don't live in a low memory world anywore and we should work ourselves up to write good variable names for the future. As for the axis, x_axis would be better in my opinion, or even horizontalAxis. As programming progresses many people that aren't from the field approach it and many fields outside programming acquire programming in their daily schedule, if a math major finds an x he might solve for it, if a pirate finds an x he might start digging, if I find an x, I would likely hope she doesn't notice me because it would be weird. In short, you should make sure that every other person reading your code who isn't in aware of your context can understand the code! :)
@@impromptu222 I can see your point, but I would still say thay things like coordinate system labels are fine as single letter names.
If, for example, you're in the code that deals with uv coordinates and you don't know what uv means then you probably shouldn't be messing with my procedural mesh generator.
I wouldn't want to work with a vector library that named the axes horizontalAxis, verticalAxis, and depthAxis instead of just x, y, and z. I also wouldn't want to work with a colour library that used full names instead of rgb or hsl, etc.
Sometimes the single letter names have a we'll known meaning in a particular domain. In which case the single letters are fine.
I'm not saying it doesn't make it harder for new people to said domain to understand what the variables mean, but even having full names in those cases probably doesn't make the code much easier either.
But, before we have a disagreement about something we don't disagree on, let me reiterate I agree that in general full variable names are a good thing. Especially when you work with developers whoes first language is not English. Abbreviations may seem obvious to some, but full words enable easier translation.
Also Fortran had pre-inizialized variables that start with specific letter
other use for single letter variable name: variables that have no real meaning
for a dumb, simple example, compare:
add leftAddend rightAddend = leftAddend + rightAddend
add x y = x + y
@@impromptu222 A mathmatician who sees a member variable be called Vector3.x will not try to solve for it.
Actually: this video is phantastic: exactly the right length, very good examples, concisely explained.
Wonderful.
I personally like refactorings that end in deleting unnecessary stuff.
There's one time that I put types into my variable names: When I have two variables that differ only in their data type. Like if I read in a string that represents a number, and then I convert it to an integer. I'll sometimes call them stringQty and intQty or some such.
Just discovered this channel and I really enjoy the way you present concepts. Other videos it's hard to sit still, but this is just done so well.
Super useful !! just when I thought I wouldn't learn anything new this opened my eyes; I have a tendency to use 'Util' classes and always thought there were ugly but did not know what to make of them, this will help me get that sorted out, thanks!
Only half-agree with the 'no utils' rule: yes, put functions close to where they make most sense, BUT also don't unnecessarily pollute Movie class with quirky business logic that you'd rather evolve separately from the class itself. Also not everything has to live inside an OO object, take inspiration from how functional languages organize data and code separately -- but close to each other where it makes sense.
You are right... I think the rule here is that if you have a Utils class, then all its methods must be static. If one of you methods needs to persist state, then you need to have a dedicated class for it, or should go directly to where it makes sense
Ok, I just watched both of your videos and noticed those were all of them. I'm amazed at how great your content already is, those are some good tips and the vibe and quality of your videos are amazing. I'm definitely subscribing and keeping an eye on it for more!
These videos are perfect to put on when you go to sleep; You close your eyes, listen to the background music and his voice explaining nicely that we all suck. And at the end we even keep the information in our head because they are presented so calmly and clearly. 🤣
4:36
Python does it. The class that all exceptions are derived from is known as BaseException. As far as I know, this is the only example. It has a subclass called Exception, which is used for all actual errors, whereas the other subclasses are more just for "edge cases", e.g. KeyboardInterrupt, which is raised when you press ctrl-C
Java also does the same with some of its stock libraries like Swing (for desktop UI), providing base abstract classes starting with "Abstract" in their names. Funnily enough, it does not do the same with exceptions, instead defining "Throwable" as the base class, deriving "Exception" and "Error" from it, and using more specific names from there onwards like "FileNotFoundException" and "OutOfMemoryError".
This has a reason. It’s to prevent catching SystemExit and KeyboardInterrupt when someone tries to catch Exception. Though name could have been better.
3:33 while Peyton has no formal type declarations, it does have type hinting. Something which is considered good practice to use in modern python code.
Good practice, maybe. But ugly as hell code.
@@yuli3873 I've gotten used to it after dealing with type hints a bunch.
In Addition, the function shouldn't be written in camelCase (PEP8) as seen at 3:33
Type hinting are glorified comments which arguably makes them worse than useless.
@@isodoubIet That’s only true if you’re using a basic text editor. The real strength of type hints comes when they are used in conjunction with a modern IDE or code editor which it can read the hints and give warnings when you’re using the wrong datatype.
counter examples:
when something is a unit of time or distance among other variables of the same units names like "t" and "x" are ok.
abbreviations: when the abbreviation is obvious and saves a lot of space it can sometimes make code easier to read, not harder. having more characters on screen also means you have to take more time to scan that same screen, i started programming long after the days of 80 char terminals, but i don't think that just being able to fit more on a screen justifies longer variable names
"don't put types in your name". Hungarian notation really pisses me off and i hate it so much ... That being said, sometimes boolean variables might have a name that doesn't work as is* and it's easier to name it as "b_*"
units: when your whole program is small and uses the same unit, i.e. milliseconds, and you know that's as specific as you'll ever need to be, or at least it will be in 90+% of the variables, it's ok to omit the unit.
prefixing with "I": i have nothing for this one
naming something *Base: suppose the derived class is templated and the base is not, that could be the reason it's been split into 2 classes, and perhaps the derived one is the one that will be more commonly used.
utils classes: this seems like an argument of OO vs procedural more than an argument about naming. Many times one may want to create simple reusable functions that deal with basic data types, and put them in a namespace like "string_util" or "math_util", or sometimes even just "util" if you can't find a better group for it. The idea functions with even only a single data type should only exist as a method is an unfortunate perception of OO design, it might not bother you until you realize that it would be really convenient to have a "bool startsWith(std::string, std::string)" availiable in a lot of places
This should be pinned
I also thought about "Node", a very general type/struct name (it doesn't tell you anything about what kind of structure it describes), yet if you use only one data structure in your program there's no need to be overly specific, generic names aren't always bad
@@r.t.5767 Thanks, agreed on "Node" and similar short names. If you have a program with only 1 kind of node there's no point in making an over-long name, and if you add a different data structure in the future you can always just rename Node, either with macros if you're using vim or with a refactoring tool
This should be a mandatory training video for every coding job.
When you’re developing in an application domain with commonly used abbreviations, such as x, y, and z for spatial coordinate axes, you should use its abbreviations. This includes code based on mathematical formulas or theorems, where, yes, single-letter variable names are common.
I wouldn't call axis names such as "X, Y, Z" abbreviations. That's their proper, full name and has always been.
Math formulas and theorems usually don't use _names_ at all. This is a common confusion between the programming concept and (esp. w/single-character variable names) the somewhat similarly looking formulas and _symbols_ used in mathematics and science. A mathematical symbol is simply that, a symbol, but of which a single character may well serve the role. It does not represent a storage location as such, for example. It may represent a numerical value, known or unknown, but not something that changes over time in general like a variable does. This is an abstract concept. An English word doesn't easily serve that role because it usually consists of multiple symbols (i.e. characters), and each of the symbols would represent a related idea/concept. Perhaps it is somewhat unfortunate that they appear similar at first glace?
@@isawadelapradera6490 Their proper, full names are "abscissa", "ordinate", and "applicate", but that usage dropped tremendously during the XX century; but my 1956 book on analytical geometry still calls them that.
Great editing, very clean presentation
Which naming convention do y'all prefer for a boolean?
a) enabled
b) isEnabled
Pro tip: b) is the superior choice.
I agree!
because it reads better:
if (Object.isEnabled)
c) isNotDisabled kek
@@Ripcraze xD
I prefer naming the boolean enabled and the getter isEnabled
b), I like when it can be read like a sentence:
if (machine.isEnabled) ...
The "I" before an interface is a MUST BE when you get to work on really big projects. Sometimes you need to find for a proper interface, and you dont even know if it exists! The fact that inside your code you can have either classes or interfaces, and to make them separated with this, is really useful and a lot of time saver. Also, you said that to the user it doesnt matter if its an interface, and thats essentially not true. When you see the "I" prefix you know at first glance that this object has only functions inside and you wont be able to get variables if you dont cast them to the proper implementator.
Something a coworker (who was like my lead in a couple projects, really amazing dude) suggested was “name variables in a self-descriptive name” a single simple rule that really made lots of sense, e.g. if your variable would fetch the size of an array then it would be something like “arrSize” or “arr_size” (depending on which case you using)
Your channel is awesome and I can’t wait to see it grow! One tip is possibly lowering the music volume. It was slightly distracting in my opinion and I totally get that it takes some fine tuning but figured I’d give a little advice if it helps!
Thanks for the feedback. I'll definitely lower the volume next time
@@CodeAesthetic thanks, keep up the good work!!
I was gonna say the same thing it’s a little overpowering sknskdnf
@@CodeAesthetic honestly, don’t be afraid to just get rid of the music. Music for the sake of filling sound can become more distracting than anything. Have it at key points if you want (it helps make an impact) but let it fade out during explanations. Also, you voice alone sounds nice and is nowhere near the grating others have that need music to drown out.
@@grepgrok8735 Complety agree with you. Found myself way more confortable at 6:40 whem the music stopped, even without voice, but coding in the screen. Apart from that, really good content, keep it up!
This video is amazing! I really love the production value! Hearing others just talk about code is very inspirational and motivating. Please don't stop making these videos!
The I in front of the interface makes it instantly clear that it’s not inheritance but implementation, and when creating a class that can be used in a function call, it gives a great hint at the programmer that they need to implement that function. I absolutely love that C# coding convention. Some conventions are just plain bad, I agree, this one isn’t imo…
I sometimes still use one-letter variable names to store a return value of a function that I don't intend to use, just as a visual clue to myself. I write a lot of small, special-purpose data analysis or protocol simulation tools, so I tend to use huge block comments with the input and output data listed, packet details, etc. with a detailed explanation of the motivation for why such code needs to now exist.
Motivation of class purpose is pretty good and often omitted habit. But in large projects I'm usually lost in how classes interact with other and where to put/find some utilities that interact with two classes at once and do not belong to neither of them. Or that extend some functionality, but that functionality does not belong to low level code and might be application specific. To many levels of inheritance is not great. I came to conclusion that all the best practices are sometimes harmful when taken to the extreme and they may work in academic examples.
Calling abstract base classes "ClassBase" in C# is a useful practice because it tells the person consuming the class that there are abstract members which they will need to implement. The only time the class should be references is in its derivations and if you want to use it as a parameter you should probably use an interface for testing purposes.
In C# at least, there are very good reasons for the naming conventions.
Exactly
That sounds weird, because the IDE and the compiler warns and shows errors about required implementations of abstract methods anyways, and if you want to extend a type, you are more than likely had a glance at the type's definition and saw whether it's abstract or an interface. Or just have your cursor at the type name and see it in the shown info I guess. Not to mention you can create abstract classes with all methods implemented, so in that case implementers are actually not required to add anything more.
Another way to approach the question is, why do you need a Base class, if as you say, you will use the interface for testing, hence you have an interface anyways? Sounds like (from C# 8) the interface could have default methods, instead of putting them into a *Base class. I've only ever have written *Base classes for this reason, and I don't write such base classes in other languages ever since. In all other purposes I guess, like to enforce some behaviors e.g. some logic in the constructor, I'd just call them their ordinary name and not *Base.
I'm happy to be corrected, but I don't see no point for such abstraction layer between the interface and the concrete class, especially since default method implementations for interfaces are possible.
Ah yes, i remember that day when i figured that C++ compiler I was using could process variable names in Cyrillic, and Used it to obfuscate code for my teacher to be unable to read it.
Like, naming one variable "c" in english, and other "с" in Cyrillic 'n stuff
I could have this line:
int xp = 10, хр = 0, ko = 15, ко = 0;
And it would work
I really dont understand most of what you are talking about, but just like I learned english by watching youtube videos for 2 years until I finally was able to understand the language I will keep watching your content until your words start to make sense for me
Sometimes, Utility class is unavoidable for normally inaccessible classes. For example, in Unity game engine, you need to extend camera, UI, or vector classes to add more functions to be more productive. Or you want to make localization easier, you need to extend string class, so you can use string.TranslateToAnotherLanguage() etc.
This video is so beautiful to watch holy shit, are you guys not amazed by seeing code, CODE, being portrayed like it’s a cinematic clip. The clips of code, the way what he’s talking about appears or disappears or gets underlined. The familiarity of format of VS code. God it’s so satisfying to watch.
Been coding for a while now but never say videos like this, definitely recommend more channels or even one of videos if you know any.
Just want to say that your music in this video is perfect. It melded together great with your presentation and I don’t feel like I missed any of the points.
I kept noticing that as well, this is a great video and the music is fantastic
Utils classes always felt a bit off for me, now finally a solution for them that makes sense! Thanks
"you don't see a bundle of utils in standard libraries"
C++: #include
Pls make more videos and don't give up on this. You're incredible! Your videos are so relaxing, your voice, the speed you're talking with, everything is perfect to me. I can even donate a little bit to support you.
Btw. What's the name of the background music?
I work in a highly frustrating codebase where many JS variables are named like "strRowId" and then it doesn't actually contain a string. Or, the variable name is prefixed with 'int' and then it's a string, instead. Or, prefixed with 'obj' only to realize that it's an array (and vice-versa). Additionally, when things fail validation tests or have no value, they are often times re-assigned bool(false), instead of a value akin to its supposed type, like null, or 0. It's extremely nonsensical.
Please don't do these things to yourself or others.
respect your sanity, go typescript
@@loko1944 or use a decent editor like phpstorm. I have noticed irritation in myself when I use VS code as the intellisense isn't as good
@@heathbruce9928 i only have issues with angular templates sometimes, which is more about their extension
@@loko1944 a really good ide still helps. Btw I use the jetbrain products. I don't get a commission
that sounds like nightmare fuel
There are reasons to use single letter variables, such as for position. You'll know what x y and z stand for because they always stand for position
Of course working with position, x/y/z makes complete sense. for other places where there is no context like that, is what is being referred to here.
even then xPos *could* be better
In most cases yes, but say if you are working on a voxel engine where you have world coordinates and chunk coordinates, it’s nice to specify in the functions that it’s looking for xChunkPos, yChunkPos OR xWorldPos, yWorldPos
Plus possible local positions within (child) components. But in general I tend to go more with "globalX/globalY/globalZ", "chunkX/chunkY/chunkZ" and/or "localX/localY/localZ". For rotational information I tend to stick with what I'm used to from CNC machines and apply the same "syntax" with A, B and C for the rotational axis.
Like that there's really no way to mix them up and whoops by that we accidentally made a vector able to describe any given position and orientation in 3D space while mostly sticking to industrial standardisation xD@@blockify
@@ABZZDEV No:
for (Vertex E : polyhedra)
if (E.x
I think the Hungarian Notation actually stems from the oldest programming language; Fortran. In Fortran, there exists implicit typing, where the first letter of a variable can imply a specific type if it's not otherwise declared. So you end up with a lot of variables like "icount" which would be implicitly assumed to be an integer because it starts with "i".
No. It actually comes from a Microsoft developer, Charles Simonyi (who's Hungarian). They were using a language (BCPL) without types, where every variable was a 16 bit word. He wrote a whole whitepaper on this. As the video stated naming variables is hard, so he focused on creating a system for it. Including the type in the name is just one thing in there.
It's not needed, if you're using an IDE to read code, but sometimes you don't have access to one. It might still be useful to see the type of the variable without having to scroll back to the declaration.
Hungarian notation, when in a typed language, was also originally designed to tell you the parts of the type that the language didn't understand, like the units. It's an integer, but is it measuring vertical pixels, horizontal pixels, vertical points, or horizontal points? If you see (hpxMargin+hptWidth) you know it's wrong. If you see { vpxIndex = hpxIndex + 1 } you know it's wrong. You can't add pixels to points and you can't change horizontal into vertical. So instead of just a float, you have "milesDistance" or "kilometersDistance" or "altitudeMetersCurrent" and you'd treat those as different types, even though they're all three floats. (But of course you wind up abbreviating the prefixes.)
In languages like Ada with better types, you can actually say "make a new type that's an integer but only interacts with other variables of this new type" so you don't have this problem.
good ole unit counter, ii
@@darrennew8211 Properly, there are two types of Hungarian notation, Apps and System Hungarian. Apps Hungarian encodes the semantic type, like vertical position, row, zero-terminated strings in languages that don't have those as a type, etc. System Hungarian encodes the programming data type, like int, string, pointer, etc. Apps Hungarian is still a good idea, but less necessary now that we have modern IDEs and can cheaply create types to indicate vertical position, row, etc.
@@darthbob88 I think unless your system enforces "newtype" (i.e., you can't interchange float miles with float kilometers) app hungarian might still be useful. Modern IDEs are definitely helpful compared to when Hungarian was introduced, for sure. I can imagine no reason for system hungarian notation even back then, assuming your language was statically (and hopefully strongly) typed in the first place.
About the 'I' in front of interfaces: The reason is not to tell the user that this is an interface. The reason is to avoid name clashes with the actual implementation.
Most of the time, you define interfaces for dependency injection. A service declares dependencies. But very often, there is only one implementation for the dependency anyway, the interface is just if you want to replace the implementation in the future, or for mocking in tests.
So we very often have a Logger or FileSaver interface, and then an implementation with the same name. So you _have_ to make _some_ distinction between the two.
Whether that is "FileSaver : IFileSaver" or "FileSaverImpl : FileSaver" really doesn't matter, and in that case I would just conform myself to the framework that I am using. In dotnet, it is the former approach.
However, I do agree that it does not make sense to prefix an 'I' to interfaces that describe _capabilities._ Like "Movable" or something like that.
It's also just a single letter that helps you differentiate if what you are looking at is a class or an interface. I'd say even if you could instantiate it directly. Costs little and adds to readability. And helps you know that what you are receiving is any class that implements a functionality rather than a class that inherits.
At least for Java/Kotlin (and maybe other JVM languages as well), you can mock concrete implementations without a problem, even if they're final. I avoid creating interfaces unless I know there will be multiple implementations.
@@cotton9126 This! Using a capable IDE it is so easy to extract an interface later, only when it is really needed. Blindly creating single implementation interfaces just adds a lot of boiler plate for no real gain.
This is a really good guide.
However, I'd like to offer a story of mine realted to 6:00 (utils)
Working in C#, I have actually found myself making a bunch of Utils classes. I've ended up doing that because I want to try and keep things organized and in a place where others know where to find certain things. So I might have things like "MathUtil" "GeomUtil" "RandUtil" "PhysUtil", so if somone is looked for a particular static function for a thing, they can find it by doing a class search in VisualStudio for "util" and find everything.
But also, a second reason for having so many Util classes is because in some cases, I obviously can't "make a new class" like mentioned in the video. So for example, I might want to make a special mod function for either int's or floats, where the negative inputs give positive outputs. But I can't just "make a new int class" in this case. The best thing I can do is make extention methods, which is still what I do. But that means I'm still going to have a static class dedicated to holding these functions. And I might as well still append "Util" to the end so it's easy to find. I could use namespacing so I can just call the class Math, but I feel like that just becomes even more of a rats nest of a problem with potentially having now 3 different Math classes I might want to use at the same time.
But ultimately, I'm not writing any of this to say my way is right and it's a water proof system. I'm writing this because I want to hear your (CodeAesthetic) or anyone else's approaches to the problem. Because trying to avoid utils for classes that you can't have edit access to is an important exception to consider.
Personally, I only create a Utils file/class when I have a handful of disparate functions that don't fit anywhere else better, but also aren't "chunkable" enough to be worth making into their own files. One of my recent Haskell projects, for example, has a Utils module containing things like:
- A ?: operator that mimics the one from Javascript with Haskell's Maybe type
- An inverse function composition operator
- A function to get the value from an Either where both sides are the same type
- Versions of the fold function that terminate early under certain conditions
- Basic string manipulation utilities
- ... and so on.
None of these things are large enough to warrant their own module (most are single-line functions), nor are they related enough to warrant splitting away only a handful, and they can't be moved into any specific context where they're used because they're used all over the place. And so, into the catch-all Utils module they go. If and when things start to accumulate in that file that are closely related enough to warrant their own module, I'll move them; for now, it's not needed.
Yeah, in C# I almost always have a class called Extensions. 90% of the time I'm not even using the class directly, so I would just do (5).Method() and the fact that Method is in a class called Extensions is of no concern at the time. Then when someone is reading it or needs the extension method it is extremely obvious that the extensions class holds it and you just need to use that namespace.
Loving this. These days where people revere complex frameworks and sophisticated abstractions, foundational details like these are often overlooked.
The number one thing I tell new group members on student projects is to *use positive boolean names*. It is very easy to get wrapped up in chained negations when programming and in my experience it helps immensely if all of your booleans (and other types too, for that matter!) are named under the assumption that "true" describes a straightforward state, even if that state is different from what the variable is expected to evaluate to. For instance, "isHidden" is suboptimal because it inverts the concept of visibility. Even if an object being hidden is its default state, you should use "isVisible" instead to make it easier to parse what is being checked for. And be consistent with this rule! I've found that just the one inversion can make your logic a mess.
Here's another example: "isNotInside" can be improved to "isOutside" -- but if you're describing an object that encloses a space, it might be more semantically meaningful to track what's *inside* the object. You can (probably) just name the variable "isInside", thereby keeping the apparent internal logic of the object straightforward.
I think the I-prefix on interfaces might be due to how how C# is kinda the sanitized Java, and Java in turn is kinda the sanitized C++. I don't really know what the chain of logic might have been, but it seems like it had to do with how Java ditched multiple inheritance and low-level controls and C# tried to claw back that space a little bit. Perhaps it was felt that multiple inheritance being messy in C++ was something that could be solved partially by properly separating interfaces and classes.
I'm guilty of doing the BaseType thing, and it's probably a habit I should break, but I find it signals effectively that the class is not for general use. Remember the lessons of self-commenting code: signal your intent. Calling something "Base" implies not only that it is abstract, but also that you should use that particular class (rather than any potential superclass) as your foundation if it matches what you want to do.
Ha! And here is the comment I was watching the video for. Because I generally use positive Boolean property names, but wondered when I had a “hasConstraints()” function. Because it is used in a heuristic, I am only interested in a decisive “false”, while a “maybe” and a “yes” can be merged in the “true” return. So for this particular case it would be more straight forward to make it “isUnconstrained()”. 😅
@@mleise8292 I'd go with the former, because that extra "un-" can throw a spanner in the logical works.
Re base types, maybe it signals that the class is not fit for _any_ use! 😅 All jokes aside, I am guilty of this, too; and although I disagree with some of what was said, in this case I plan to change my ways.
@@creativecraving I don't think I will, personally. I worked at a place where the convention was to have subclasses share at least one word with their superclass whenever possible. So the abstract class might be called WindowBase, then there would be a concrete Window class, and that one might subclass into a DualWindow class, which in turn might be the base for a DualScreen class.
Okay, so this is kind of a messy example. But it made sense for WPF stuff. The point is that it was easy to tell by sight if there was any inheritance going on, and made naming easier.
1:29 "You spend more time reading code than writing code" come to think about it, it's so true
Regarding variable names inside functions. I found that if the function is pure and adequately named and is concise (does not aim to do 50 things as once), then the variable names can go ahead and have single-character names.
yeah, if a function "print" takes only one argument "String s" then single character name is quite a way to go I suppose
I appreciate the `delaySeconds` callout. It aligns with my thoughts on unrefactored code where I will prefix things with a "namespace" or larger container first approach. ie `delay_seconds`, `delay_minutes`, `event_click`
Except click is always an event; close_button_click would be more descriptive.