Hearted Youtube comments on Kantan Coding (@kantancoding) channel.
-
3200
-
2200
-
2100
-
827
-
625
-
531
-
305
-
281
-
277
-
268
-
218
-
217
-
166
-
162
-
147
-
146
-
127
-
as a lead dev, I can only say that you never want to debug such code, which contains 10 small functions, because someone decided to split everything. It is a huge pain to debug such code, because you have to switch context a lot. You have to switch between function, classes, files.
Sometimes it is much better just to have one bigger method instead of 5-10 small. Sometimes it is always better to not create an abstraction for every single potential change, because many of them will never happen.
For example, it doesn't make sense to create an abstraction for delimiter, because delimiter may never change. But sometimes it makes sense to create such abstraction, haha...that's the hardest part of programming - to find a good balance and write readable not overcomplex code.
It is not hard to create overcomplicated code with many abstractions, but the real problem is to create easy readable code for complex solutions.
114
-
111
-
108
-
99
-
93
-
73
-
72
-
71
-
69
-
68
-
67
-
67
-
66
-
65
-
64
-
58
-
58
-
55
-
53
-
50
-
48
-
47
-
47
-
47
-
46
-
45
-
I really dislike the common wisdom about "self-documenting code" as it is proven again and again by examples that are the simplest of simple code.
I think it all starts somewhere different. And that's the distribution of logic into multiple methods when it does not need to be that way.
When ever something gets so big that you might add in a comment, someone will say "just split it into descriptive methods", and yes, in the spirit of uncle bob you can split that 30 lines method into 10 simple methods. And instead of reading the code from lines 30 to line 60, the dev now has to scroll around trying to memorize 10 methods and what they do and build a mental model of how they are called. And after splitting one method into 10 methods, it's suddenly too much work to put a doc header of them, so we don't do it because we don't like tying shit after we just split that shit up for no reason.
Three steps back. Lets not split up code if there's no reusing of the methods. Let's keep the code local, in context.
Complex code exists, because complicated tasks exist.
So you got complex code. Your coworker asks "what dos that do?"
What do say? Do you go line by line saying things like "so this line checks if the first parameter is null" and so on? Of course not. Your coworker can read. Human language can is great to explain the concept of something. You will probably be able to formulate one sentence that can explain a complex block of code. Well, if you understand that code you should.
And that's a good comment. Because three years later, someone will search for a bug and will find that block of code and ONE GOOD LINE may explain it better than anything else.
"So what if the comments lie?"
Comments are written with the assumption that code does what it is meant to do. Comments may "lie" when the code does not what it is meant to do, but in that case, the bug is the discrepancy between intended and actual behavior. You got two versions now, one telling you what it does, and one what it is supposed to do. You can now check if the assumptions where wrong from the beginning or if it's an implementation error and the fix is going to make the code do what the comment tells.
That's my experience in working with complicated code.
44
-
44
-
44
-
44
-
41
-
40
-
39
-
I see some people complaining about the code extraction. In the example, I would do a similar extraction, but for another reason. Avoiding code duplication is a nice side effect, but the core idea should be: Keep the abstraction level of a function or class or whatever constant. An example: You don't want a function dealing with structuring a report page doing regex stuff, this is clearly too low-level. Think about how you would explain your function with a few words to your colleage: "I look if there is a report header, else I use the default, then I add the body, and if there is a footer, I include it as well". And that's what the function should do. If it does some finicky stuff with the body, extract it. If it does some regex, definitely extract it, maybe even a level deeper, below the functions dealing with the header, body or footer. Having one abstraction level is much more important for readability than to avoid code duplication.
36
-
36
-
34
-
34
-
33
-
32
-
32
-
31
-
31
-
31
-
31
-
30
-
30
-
29
-
28
-
27
-
27
-
27
-
26
-
25
-
25
-
24
-
24
-
24
-
24
-
23
-
23
-
22
-
21
-
21
-
21
-
20
-
20
-
19
-
19
-
18
-
The long names for tests are actually pretty standard.
It's not so much about the long name as it is about documentation.
A well-tested code-base can contain thousands or even tens of thousands of tests and when the business logic becomes a bit more intricate than these examples you can have multiple failed tests for one seemingly simple change. Being able to read through the test names and understand what their testing without opening each one up to read comments or the code itself saves a lot of time and keeps you from getting distracted from your actual goal of fixing your regression.
Another reason for verbose test names is that a test tends to be more granular than a method would normally be. You could potentially test 10 outcomes for one, let's say 10 line method.
To use one of the tests in the video as an example:
'TestIsBlankStringShouldReturnTrueWhenBlank 'could be shortened to something like 'TestIsBlank'. This would work fine in the video because it's just used as an example, but you would normally test more than just the one outcome. You'd need to test that the method behaves correctly if a null value is passed in or that it does, in fact, return false if the string is not blank and not true as well.
Never mind languages that don't have type safety like javascript where you'd have to check if the method handles integers correctly. What would you call the methods for these tests, considering that 'TestIsBlank' is already in use?
With that said, most test frameworks have a some sort of description feature that would display the description instead of the test name in the test-runner. The descriptions act as a sort of comment.
Sorry for the essay, but there are a lot of intricacies in writing maintainable tests and rather specific reasons for things that look arbitrary or unnecessary. Much like test names, the explanation as to why they are long is also, well, long.
18
-
18
-
18
-
18
-
18
-
18
-
17
-
17
-
17
-
17
-
17
-
17
-
17
-
16
-
16
-
16
-
16
-
to much animation. going back and forth multiple times was making it hard to keep track, because you expect a forward progression, but then be pulled back unexpectedly, then forward again, you quickly lose the sense of knowing what to expect. furthermore, the second and third techniques were glossed over compared to the first one, which feels imbalanced and incomplete. however, the overall structure was well, as well as the individual animations, and the examples as well. im sure some minor changes will make the production quality even better. it seems like you forgot to account for how the audience will process the content, getting absorbed in your own understanding, which is a very common pitfall. all that aside, i am looking forward to the next one!
16
-
16
-
16
-
15
-
15
-
15
-
14
-
14
-
14
-
14
-
14
-
14
-
14
-
14
-
14
-
13
-
13
-
13
-
13
-
13
-
13
-
13
-
13
-
12
-
12
-
12
-
12
-
12
-
11
-
11
-
11
-
11
-
11
-
10
-
10
-
10
-
10
-
10
-
10
-
10
-
9
-
9
-
9
-
9
-
9
-
9
-
9
-
9
-
9
-
9
-
8
-
@kantancoding What I was saying is that comments provide "a" layer of guidance and confirmation, not "the" confirmation. Yes certainly, there should be test cases. But in practical terms, the struggles around not knowing which is wrong, the code or the comments/tests is still valid in both paradigms, whether you're taking about comments or tests cases.
Either way, when the bug report comes in, someone has to find the bug. When reading that code and wondering "hmm is this < supposed to be a <=", or "is this parentheses misplaced", or "is this parameter wrong", its much, much easier to read the nearby comments and gather the intent than to flip back and forth to another file elsewhere (like a test case) to try to figure out what the commentless "self documenting" code was supposed to do.
The part about "self documenting code can eliminate the need for most comments" is the fallacy. Sure, when you're the one writing the code, your own comments can look somewhat redundant. But that's cognitive bias coming from already understanding the code and whatever idea it was implementing. To someone else debugging that code their first time, those comments can be valuable.
The bandwagon of "self documenting code" and pressure to comment less seems to keep coming up as if its an answer, but it isn't. It's part of the problem, and its bad. Just like a dev who doesn't update comments is bad. I don't make excuses for either of them.
You are right in that comments can be poorly written. Writing good comments is a skill, just like coding itself is a skill. But someone else's poorly written comments aren't a validation to this movement of discouraging comments in the name of "self documentation".
8
-
8
-
8
-
8
-
8
-
8
-
8
-
8
-
8
-
8
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
7
-
6
-
6
-
6
-
I really need to say this. The advice to never nest your code has been repeated for years now, it's the same thing that I was told 8 years ago when I was reading about how to write VB code in highschool and it is a good concept, but It's important to note something.
In the provided example, yes, I think the code is easier to read, but it does not change the conditions you need to keep in your head. If you are doing more complex logic that then relies on the state of those conditions, you still need to know that those conditions are not true for example.
Take authentication. If you have middleware the runs in an endpoint, or your function verifies that the user is authenticated before running, you still need to know that condition has been met, otherwise you will end up writing redundant code in your actual logic to ensure that the user is authenticated. Whether your nest or don't nest the condition, you need to be aware of the state your app can be in at that point in the code, and both ways require you to have this awareness.
I agree that in most cases, early returns are better, but just wait until you have a function that has a structure like this and tell me it isn't confusing AF
if (A) return
// logic
if (B) return
if (C) return
// logic
if (D) {
//logic
} else {
//logic
}
if (E) return
//logic
The code looks readable, but damn, it can get confusing, especially if the code I put as "//logic" can potentially return, therefore adding conditions that aren't even ovbious. Sometimes the real solution in my opinion is to double up your conditions just for understandings sake, or in a complex case, abstract where necessary. The idea of code inversion is very primitive and does not always apply
6
-
6
-
Not that example specifically, but being able to treat functions as variables is amazing in general.
I wrote a differential equation solver for a university project once. There are lots of methods to do that numerically (approximating solutions) like Euler's method, AM2, AM3, AB2, AB3, BDF-2, and more. I could then implement one step function for each method, and one solve(...) function that took one of the step functions as an argument as well as initial conditions, and then it took steps until it was done. I think I even made it a generator, so I didn't have to save the entire list of potentially 100,000s of steps if I just wanted the max y value or something.
In general, it's a great tool to make template functions and separate out the specifics into their own functions.
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
5
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
Just like the others, I have to say this channel has such inspiring content and covers things that I don't think others do, and in the most educative way. Don't worry, the like button is history. Honestly, I had no idea these endpoints were available.
I also think they're not going to be free long, just like Github Copilot, so, while the getting's good :D ...
I have to mention that I still don't think I'll be churning out apps really soon, and not as easily as others, but it gives me a sense of how much or what I need to be capable of, and what I need to stop hesitating to do (living in a deteriorating economy).
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
That's a long, but useful explanation why you need to give your functions good names.
If I'm using more generic functions that I can't rename, I like to define variables that explain what is being done. Sometimes, I even split things up, even if I could chain them, if it's not causing big performance losses. This makes my code look more like "const, const, const, ..., return". You should only have to understand the left side then and can put debug statements in there easily. Here's an example in JS:
function getAllMembersEligibleForBadge(badgeName) {
const allPeople = getAllPeopleFromDatabase();
const allMembers = allPeople.filter(isMember);
const badge = getBadgeByName(badgeName);
const eligibleMembers = allMembers.filter(p => isEligibleForBadge(badge, p));
return eligibileMembers;
}
3
-
5:00 no offense but creating a function called saveFormatted that has the only responsibility to call ... saveFormatted... huh?!
such a junior mistake. You already have a function that does the job, call it, that's it. No need for an extra function that exclusively calls the function you wanted in the first place.
Same for splitting formatAndSaveName
It only makes sense because you completely ignored the "call" side of those function, and unquestioningly consider any side effects to be bad.
Everywhere you called that function, you now have to call both format and save, which both makes it a huge source of bugs, by making it easy to forget one of them, and completely misses the point of API abstractions.
Not only that, but the fact is, you'd have to call them both everytime or so, making the idea of separation of concerns really not apply here.
Formatting and saving are really really really related concerns, not separate, when your task is to format and save.
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
2
-
about "avoid deep nesting loop", I believe there will be some very rare situations you can't dodge it, for example can you solve this problem without nesting many times? I doubt about it
Given an `m` x `n` matrix, find all 2-dimensional subarrays of size `p` x `q`, (`p` < `m`, `q` < `n`) such that the sum of the elements in these 2-dimensional arrays is the given `number`. Returns a 3-dimensional matrix that is a combination of the 2-dimensional matrices just found
prototype: int*** matrix_3D_res(int** matrix, int m, int n, int number, int& d1, int& d2, int& d3)
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
First 5 seconds is dead wrong. Some of the best developers in the world nest their code several layers deep. Why? Because that gives the best performance, and is often the easiest thing to understand and refactor, especially in early stages of development. You know what's difficult to reason about? Inheritance and prematurely encapsulated logic - which is often promoted as the remedy.
But it's okay (and expected) to not be able to reason about nested code (or large amounts of code in general) at first, and wanting to extract snippets to something that gives you more mental clarity. Just know that it's not categorically better to avoid it - it's situational and depends on the experience of the team.
0:32 And as far as inverting the if statements go, that is just as easy for me to reason about, as someone with a decade of experience. The main difference is that now the unlikely error states takes precedence in the function, and most times I need to read the typical cases. Which means I can now see less of the real meat of the function while also seeing the useful summarizing function name. It's a minor difference, but I think there's good reasons to disagree with your statement here.
2:13 There's no way that's a confusing if-statement, unless you're an absolute beginner. The good variable names give it all away. The only way it would be confusing to me is if those variables had ambiguous names. Extracting it to a function suddenly makes it a non-zero risk that the function is doing something unpredictable (unless it's guaranteed to be a pure function), so when debugging it, you now have to navigate to the isValidUser function to verify that it doesn't, which results in additional cognitive load, not less.
But the calculateTaxes() example is not the worst. Except I think the entire "main" function there seems to be the function that should be called calculateTaxesForCart() so there should be no further clarity needed - especially not for such a small function.
4:05 It's not the best example, because it's really slow to do a list of single user queries if they're not cached. Batching is always better. But if we ignore that part, I think you're missing the essential guidance of when to make something reusable. Because premature reusability is a common source of a wasted effort for developers. And the answer is: wait until you have actually done the identical logic two places in your code. And three or four times if it's something fairly simple. And note that if the function is not pure (i.e. giving it the same parameter values can yield different results), then it's harder to reuse safely unless the function's purpose is abundantly clear.
4:38 I categorically agree with good naming, but I'd go further and say that it's better to error on very long names to clear up any ambiguity. The only good reason to shorten names is if you're creating a temporary alias shorthand for quickly modifying a complicated algorithm over many iterations. Some experience developers also think it's okay to mirror the mathematical letters in the paper of an algorithm that was applied, but I personally disagree and think you could add those as comments instead. Naming can be really hard, but it's a vital skill to keep practicing as a programmer, because six months after writing the code, you'll also find that even you don't understand your own low-effort-naming very easily. Additionally, even absolute beginners probably realized quickly that naming was important, so I think it's more useful to provide more clarification and examples than just that statement.
Lastly, these are not remotely "laws" - and that term is preached far too often within developer circles. I think the only thing that approximates an actual law in computer science, is Conway's Law. I.e. that the structure of the software strongly correlates with the structure of the team(s) that built it.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
okaay, i have finished the 2 parts, i found it very useful, but for suggestions and to deliver the topic completely, i think we need a full backend project implemented with this arch, we need to see a real-domain implemented and used, I am now stuck on how to implement a banking-system with this arch, where to put what or how to orgranize the domain ?
I don't say that these 2 videos didn't deliver the concept, actually they delivered it so clear to me, but we need more real-life scenario, And thank you for your amazing effort.
2
-
2
-
2
-
Readable code, schmedable code. Make updated comments a requirement before code gets merged. Yes, comments can lie, but so can variable names, which objectively are just memory addresses with built-in comments. Their names are not for the computer, they are for you the developer. Make comment changes part of code review. You could create a function just like the one at the beginning, but if there is no circle that needs it's area calculated but some thing else that has a virtually identical formula, is the code really self-documenting? If we are okay with updating variable and function names, updating comments should be no different. Ontop of that, If I have a game that has 30 enemy types, and each attack pattern is created by a function, should I change each function name to "consecutiveNormalPunchesAttack" or "falconSmashAttack"? What if each has to conform to an interface? Should I make a private function that has the sole purpose of documenting what the attack is supposed to be like? Perhaps if there is some extra special logic, such as randomly chosen attacks that each have their own complex logic, but if I have a 20 simple enemies that basically have only simple but unique attack function, I think making extra and redundant functions is only going to make a file longer, bloated, and harder to follow. I'd rather have a clear comment that explains what the code is supposed to do, which is what the real purpose of comments is. It isn't about what the code does, it's why it's there.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
@kantancoding hey I appreciate the response! And I hope I'm coming across as helpful not just negative!
I've extensive experience teaching junior engineers, and like you say the senior engineer understands what you're saying and can contextualise it. The junior engineers cannot, and so what I mean to say is the examples given can lead people astray.
Dependency Injection would absolutely make that code modular in the way it was split - I might be wrong but I don't think DI was mentioned in the video, and this is a key detail here that is often not obvious to juniors.
A similar detail that can cause a misconception is avoiding hard coding the company by extracting to a constant, but the constant is still hard coded - it's just not inside the function any more. Similar to just moving code into another function does not produce modularity, moving a literal to a constant does not remove hard coding. Again, a senior gets that but a junior might not.
Unit testing against a public interface is more than my opinion, it's more or less the only way that unit testing is generally useful 😅 you can test implementation details if you like but you'll just end up writing your code twice every time, which is where many people go wrong with TDD and end up jaded on it. You want to treat your code as a black box, and only this will produce the very real and very powerful positive outcomes you mention in your video.
Public in this sense means where the "seams" in your code are, in other words the public edges of your abstractions. Usually in C# or Java worlds this is an interface, in JavaScript it would be an exported function. If you are exporting a function, or promoting a class member above private just so that your unit tests can see it, this is often an indication that you're testing implementation details.
When you decide what a piece of code does, you encode its contract with the world in the tests you write against it. The tests (like you very rightly pointed out!) then form machine verifiable documentation to others about how to use the code and what it does and does not do.
So in that sense what we mean by public here is what does a caller need to know and care about, and what shouldn't they know and care about? And this is the key art of software design, i.e. choosing useful, meaningful abstractions that effectively modularise your code.
I enjoy your format, and I think you're sharing really good ideas! I do think these kinds of details are important if you are aiming to teach your ideas accurately (based on my own learned experience teaching poorly on many occasions!). I hope it helps!
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
Sounds good, doesn't work.
In theory it can work, but the following is what will happen int the real world:
You may be enlightened, but your colleague could not care less, and even if he did, he's barely finishing until the end of the sprint, so in the average case to even suggest having unit tests at that point which will delay results will be frowned upon.
Even if you over plan to make sure you have time which is unlikely, expecting that your colleague will modularize the code for the unit tests is the second funniest thing I've ever heard.
What your colleague will do is mock the soul out of the code, and write the least amount of tests that reach the minimum coverage, that test nothing but the mocks themselves, and will consequently be very fragile, but not to bugs, but rather any code change that's not a comment. Sometimes they'll also be flaky, which is the funniest thing (at least in hind sight) I've actually experienced (yes, a unit test can be flaky if you're good/bad enough)
Trust me you don't have enough time to make your colleague do it in significantly more sensible way if you wish to complete your own work.
So let's say you go and refactor it like the enlightened person your are. Well there is just one issue, it turns out that less than 10 percent of the code is something that you could theoretically move to a pure function without side effects, as everything wants to poke the system, and it's spread out, so zero pure functions to extract for you, and simulating the "poked system" is questionable, so it turns out that you just made more small tests that test the mocks. I guess at least you'll have to fix smaller things when you change anything to not catch bugs.
Let's say that you're actually working on this nonexistent thing that is just full of pure functionality without side effects, it's like the culmination of leetcode as a bunch of logic.
It's your time to shine.
When you finish, you feel uncle Bob's proud acknowledging hand on your shoulder as he sheds a tear of pure bliss.
You did it, created perfection.
That night you have the best sleep ever knowing that you basically brought salvation.
One month later you're really busy with other stuff, or sick, so your colleague has to implement a feature in this ultimate existence of code. The first thing your colleague notices is that he has no idea what the hell is happening, because you have 3 line functions. The names are descriptive, but his monkey brain can only remember about the last 4 calls, and has no idea what context he was in a minute ago. He check git blame and identifies you as the anti-christ, but does nothing about it other than remembering this for the rest of his life. Finally he finds what he needs, and he puts in what he needs the quickest and dirtiest way you can imagine, and goes with it. It's not that he could not follow the patter, he just got way to annoyed even before he found what he needed to do it.
He tries it, it works, he commits. The pipeline fails. The annoyance he felt before but almost faded completely comes back and goes into overdrive. Now he's really frustrated.
He check the tests and contemplates fixing them. He even starts and fixes one with an unnecessary mock, but when he reaches the 2nd one out of the 4, he realizes that he'd have to do something that's not necessarily hard, but he considers to be an extra effort for something he already despises. The result is obvious. 1 test is deleted, one is commented out, and one is disabled (not to mention he did not write any for new stuff). Why not the same treatment? He doesn't know either, it just happened somehow, but the pipeline is green now.
You're busy / sick, so you don't review it, but the others will stop this madness from happening right? Riiiight? Well, we all know the answer,
After some PR comments 2 typos are fixed in the commented-out test code, and an extra todo comment is added to the commented out and the disabled test.
The PR is merged.
It is now true production code.
Ask the question. Was it your colleague who made tings go sideways?
Well, yes, but for him you were the one who set him up to failure.
It's supposed to be a relatively small tool but there are about 50 files with countless functions, for him to understand it would take more time then to code It from zero with a similar amount of bugs due to the size.
Sometimes less is more.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
Great video, short and concise.
But here I'm just doing integration tests these days.
I mean, in the code of the example, the database access part is not tested. Doing mocks for testing is just wrong IMO, so you endup writing integration tests anyway. So I might as well test all the branches of a piece of code in one integration test.
I used to do both unit and integration tests, but really it's not worth. Doing unit tests you endup using mocks, and it can sum up a lot of code at the end, and more code is always a bad thing. Specially in the case of Go, to test with mocks you have to use interfaces. Adding an interface just for the sake of unit testing makes it all more difficult.
Now I don't use interfaces nor mocks, just actual implementations. I use docker to start real components and write a client for my API/service and the tests use this client.
I might write a unit test for a specific function, but for the most part I do integration test and never use mocks anymore.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
Hiya,
I can relate to what you said in this video: I watch tutorials but never implement them. These days, I am trying to solve problems. I realized that I am not that good. For example, I want to find the subset of a string "abc" and I am unable to do it.
I know Python, but honestly, I don't know when to use it. For example, sometimes I use class at a place where it is not necessary; sometimes I don't use it where it is required. I believe "practice" is what I lack, but I am not sure.
Could you please give me some tips to be good at coding? I would love to write fast and efficient code, but I am not sure how to improve that skill.
Do I need to practice solving problems that are there on the internet daily with respect to lists, dictionaries, classes, etc.? Would that help me improve my problem-solving skills? Or do you think I need to take some courses online?
Any suggestions are hugely appreciated. Thank you.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
Okay I'll give you a more laymen term reason on why even learning pointers is important then go into an example, because at first glance it may seem like you don't need them, but this is widely dependent on the language you are using. All procedural languages have two forms of passing shit around, pass-by-value, or pass-by-reference. Essentially, it means by standard, are variables holding a reference to a value, or is it literally just the value?
In C for example, it's pass-by-value. So if I have the variable 'age' and I have a function called "HappyBirthday()", I probably want that function to add 1 to the age variable correct? Well if I simply call that function like so, "HappyBirthday(age)", it'll add 1 to it...but it'll be adding 1 to the age that was created when the function got called (this is due to a new stack frame being made, google "memory map" if it confuses you)...because it's passing the value of 1, not a reference to age.
Thus when the function completes, the 'age' variable actually won't be affected. So you need to pass in a reference to age by giving it the address of age, so that "HappyBirthday(&age)" can target the value at that address and add 1 to that instead.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
the worst day in ur life possible. don't think it's cool. after 2-3 month this will become ur nightmare where u don't have ur time in ur life, and ur life is under company. ur brain, ur relationships, ur body, ur health, mental health will be fucking destroyed. don't wish such a job. it's not about free people, it's about slaves and nolifers without money also, if u think 2-10k usd a month is money. so you will not have time to spend those money, maybe for doctors and pathetic tries to have vacation 1-2 weeks a year thinking about that nightmare will come back soon. this is not a life, people shouldn't make it romantic. i'd rather be homeless with no property but free. it's too important if you don't get it now, u will in the future.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
To be fair, a lot of what is shown in this video is not needed or even recommended for beginners, since that is the group of people that can benefit the most from understanding and using the built-in features that plugins often seek to replace.
You can use :define and :include as a replacement of "Go to definition" and "Go to reference" of language servers. However, if you really need languages servers, I would suggest to simply install neovim with its native LSP features instead of running COC which is slower and more error-prone.
Instead of nerdtree, maybe you can get away with :Ex, :find, :h args and :h buffers. When you actually have a need for faster navigation, fzf.vim (also shown in this video) is really good or command-t. In many cases, you are actually searching patterns within your code base and not simply files. Therefore, it would be more appropriate to use :vimgrep and :grep.
Adding plugins for language support can be a bit too much when you only want compilation. Behind the scenes, such plugins set little more than a decent :makeprg and :errorformat, which you can configure on your own according to your requirements.
As a replacement of code formatter plugins, vim provides :formatprg to run external programs. As an alternative, visually selected text can be piped to external programs via !{motion}{filter}
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
Rule of thumb is, less is more.
Focus on delivering functionality in a black box. Dont mix up the variables, classes or objects.
Set up clean, basic functions that are hyper focused on delivering base of your program. Make it as barebone and simple as it gets to get up and run your functionality properly (simple means done by the standard, and not fucking ducktaped all over the place).
Keep the base locked away from rest of the program, add additional details and tweaks as another objects, if you need to modify the base of your program, setup it through functions, put a proper comment above. It has to be precise comment.
Every testing modification should be injected from different enclosed space. For example functions and methods do the job here quite nicely. Dont mix up the code that is already working, since you will destroy your ability do debug. With bigger projects, this will lengthen your time spent on finding bugs from 1 hour, to whole week (trust me on this).
Ship every new addition to your code, as build up layers upon the core of your program. Its the most important part, do not interfere there unless it is the only option. This way you can roll back functionality without rewriting the core of your application. Use wrappers to do that.
Every new functionality should be a working black box. NEVER expose singular variables, functions, methods, or anything like that. Setup your INPUT and OUTPUT. This way you can work on applications as a team, every single programmer can work on his "brick", without interfering into others work.
Use every tip mentioned above, to have perfectly readable, and reliable source code, that can be modified with all functionality, without breaking down, or having any memory leaks. It will always compile, and always keep the bare minimum to boot up, and work.
Only then, if your parts of the enclosed code are rock solid, and are not to be swapped, you can get down to optimizing process. It will destroy the readability of your code a little bit, but at that point of development it probably wont change anything.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
Apply multiway (k-way) merge of external sorting which M is 3 and K =3 .
39, 12, 20, 34, 55, 45, 67, 89, 56, 88, 99, 9
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
okay no. that first one could have been good advice, but you're reasoning for even trying to avoid deep nesting is so off. doing those inversions on so many if statements doesnt give you less if statements to process, because you still need to be aware of all those steps, all it does is make it less clear that they're all tied together. you've made the problem worse. in some contexts, yeah inverting your if statements is great. the example you're giving is 100% not that case. it was easier to read before you did that, because it was easy to tell that all those if statements were conditions before the core code could run
better advice: add "and" to your if checks. you can cut down a line if you do this, having less if statements and less indents, but personally I still only check for two values at most if possible, otherwise you're actually needing to remember multiple checks at once. in the nested block you show as an example, you dont need to recall each piece, short term memory at best, you can focus on each line and say "after this is true, then we check this. if this is also true, then this-" and after each if those ifs you can push the previous line out of conscious thought
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I love functions and I think functional programming should be used more mainstream. That said, in the imperative domain, I find that extracting the predicates can often result in less readable code. If you're not using those predicates in other functions, they clutter the source file and usually they're just an unnecessary indirection (e.g. 'isEven(x)' is not significantly more readable than 'x % 2 == 0'). It can look cleaner when everything is behind a nice function but quite often I'd prefer to have "the guts exposed" and in one place so to speak, especially in the imperative domain. If you really want to name the predicate (e.g. isValid := isAuthenticated && isAuthorized) or if you use it multiple times within the function and want to avoid repetition, I think a lambda function (or even a nested function if your language allows them) is better than defining a new function in the outer scope. If you do this, I don't have any objections about extracting as long as you can actually name the thing sensibly.
Which leads me to the other thing I wanted to comment. I don't disagree about naming things meaningfully of course but that's easier said than done. I think often it's just impossible because the things people work with can be highly specific and far removed from everyday reality. I might also have a slightly deviant interpretation of what it means to give things meaningful names, e.g. I think it's perfectly fine to use one letter variables when the variable represents something generic (find e (x:xs) = ...), and ofc the classic i, j, and k for loop variables. Also longer names (classic OOP trope) doesn't mean they're better; consider the "BeanContextServicesSupport.BCSSServiceProvider" class or literally anything else from the Java API. I'm sure whoever named that did their best to name it as clearly as possible (still I wonder what's the point of adding the seemingly redundant "BCSS" there instead of just "ServiceProvider"?) but things like that just can't be named "meaningfully" to anyone who isn't already familiar with "BeanContextServices" and so on. I'll end it here before I start rambling about how terrible OOP is again.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
The three laws of bad or incomplete advice:
1. There are no laws in programming, there are opinions, alternatives, guidelines, and other
2. There are significantly more than 3 general guidelines for most programming related topics
3. There are exceptions which depend in many factors such as time, cost, performance requirements, technology used, number of users, how long the tool will be used, and more.
4. There are more than 3 rules or laws about bad or incomplete advice
The advice provided is helpful however, avoid saying that these are rules or laws and that this is it, just three, keep it open to possibilities and exceptions.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
Someþing pretty funny I realized is ðat in C programming ðe use of "goto" is accepted for error handling. You see, when you call functions that might fail, you read its return value and if it fails you, obviously, handle ðe error. However, in ðat error handling you might have to do some cleanup and closing, and you might end up repeating a lot of code when writing your error handlers.
// for example
int get_some_strings(){
size_t len = 0;
char* str1 = NULL;
if(getline(&str1, &len, stdin) < 0){
return 1; //ok
}
char* str2 = NULL;
if(getline(&str2, &len, stdin) < 0){
free(str1);
return 1; // still ok
}
char* str3 = NULL;
if(getline(&str3, &len, stdin) < 0){
free(str1);
free(str2);
return 1; //well
}
char* str4 = NULL;
if(getline(&str4, &len, stdin) < 0){
free(str1);
free(str2);
free(str3);
return 1; // oof
}
free(str4);
free(str3);
free(str2);
free(str1);
return 0;
} // See how ðe cleanups stack up?
To solve ðis problem, ðere are two techniques, one is just creating a function for cleanup like ðis.
void cleanup(char* s1, char* s2, char* s3, char* s4){
if(s1 != NULL) free(s1);
if(s2 != NULL) free(s2);
if(s3 != NULL) free(s3);
if(s4 != NULL) free(s4);
}
int get_some_strings(){
size_t len = 0;
char* str1 = NULL;
if(getline(&str1, &len, stdin) < 0){
return 1;
}
char* str2 = NULL;
if(getline(&str2, &len, stdin) < 0){
cleanup(str1, NULL, NULL, NULL);
return 1;
}
char* str3 = NULL;
if(getline(&str3, &len, stdin) < 0){
cleanup(str1, str2, NULL, NULL);
return 1;
}
char* str4 = NULL;
if(getline(&str4, &len, stdin) < 0){
cleanup(str1, str2, str3, NULL);
return 1;
}
cleanup(str1, str2, str3, str4);
return 0;
} // much better
And ðe oðer is using gotos to jump to ðe cleanup section
int get_some_strings(){
int rv = 0;
size_t len = 0;
char* str1 = NULL;
if(getline(&str1, &len, stdin) < 0){
return 1;
}
char* str2 = NULL;
if(getline(&str2, &len, stdin) < 0){
rv = 1;
goto cleanup1;
}
char* str3 = NULL;
if(getline(&str3, &len, stdin) < 0){
rv = 1;
goto cleanup2;
}
char* str4 = NULL;
if(getline(&str4, &len, stdin) < 0){
rv = 1;
goto cleanup3;
}
free(str4);
cleanup3:
free(str3);
cleanup2:
free(str2);
cleanup1:
free(str1);
return rv;
} // also better
Each way of avoiding repetition in your code have some advantages over ðe oðer (using a cleanup function tends to look more elegant and makes your main function less cluttered, while using gotos avoids function call overhead and is easier to refactor).
1
-
1
-
1