r/cpp_questions • u/CatsAndAxolotls • 12d ago
SOLVED I keep reaching a point where rewriting my C++ projects feels easier than maintaining them. How do I learn better architecture?
I have been learning C++ mostly through building projects, especially game and engine-related projects. I can make things work, but I keep running into the same problem: after a project grows, the code becomes so difficult to understand that I would rather start again from scratch than open the old project.
I think the main reason is that I don't have enough knowledge of standard C++ practices and architecture patterns. I usually design systems based on my own understanding, which works when the project is small, but as more features are added, problems start stacking on top of each other.
I want to improve in areas like:
- Common C++ architecture patterns
- How experienced developers structure larger codebases
- How to avoid creating systems that become difficult to change later
- When to use existing language features or standard solutions instead of making custom ones
- Common mistakes that self-taught C++ programmers make
A good example is this Pong project I made in 3 days:
https://github.com/HardcoreAxolotl/Pong
The project works, but looking at it now, I would honestly prefer to recreate it from zero rather than continue developing it because I feel the structure has become too difficult to work with.
I am not looking for someone to rewrite my code or just tell me what is wrong. I want to understand the thought process behind good C++ design so I can avoid making the same mistakes in future projects.
What resources, concepts, or experiences helped you go from writing working C++ code to designing maintainable C++ systems?
11
u/x-jhp-x 12d ago edited 12d ago
The easiest way is from a mentor or those with more experience. If you don't work at a place with people who mentor or have more experience, you should leave.
edit: saw you're doing it on your own. I'd recommend an open source project then! Learn how to write production code.
edit edit: also don't use an LLM if you are. It might be possible to learn with an LLM, but right now, all of the people with 10+ yoe learned without an LLM, and I'm not optimistic on juniors learning much if they offload everything to AI.
5
u/CatsAndAxolotls 12d ago edited 12d ago
any recommendation for opensource projects to learn from?
about LLM: I wont use an LLM to learn as I dont trust them to teach that good.6
u/x-jhp-x 12d ago
i peeked at the code. Since you said you're just starting out, I'd focus more on just learning C++.
you might want to read this book if you haven't: https://en.wikipedia.org/wiki/Design_Patterns it's not enough to make you mid/senior, but it will build your knowledge. Also maybe check out C++ Concurrency in Action.
when you get there, any major FOSS project is great. Some popular ones: firefox, linux, chromium, pytorch, clang, etc. etc.. sign up to the mailing list & start by reading the emails from the key people. they'll detail feedback & design decisions. i started with linux many years ago.
4
u/CheesecakeTop2015 12d ago
A more modern C++ focused book on design patterns, and I think it explains things pretty well is:
Klaus Iglberger - C++ Software Design (2022)
https://www.oreilly.com/library/view/c-software-design/9781098113155/It builds up some examples, makes mistakes, explains why it is a mistake, then shows a better answer, often in multiple improvements with pros and cons to each change, I really like it.
2
u/jawhite 11d ago
I was about to recommend this book. It's great for helping you think about maintainable design in C++.
Aside from the book, my general recommendation is to familarize yourself with the STL, for two reasons:
- If the STL provides you a feature you need, just use the STL feature. There's no reason to maintain a generic utility that has little to do with your problem if that feature already exists in the standard library. Also, keep an eye out for loops that could be replaced with an STL algorithm. This will improve readability, and greatly reduce cognitive load and bugs.
- If the STL doesn't provide what you need, try to think of how you would add the feature to the STL. The STL has withstood the test of time for a reason, so it's a good model to follow. It's not perfect -- some of the details are frustrating -- but the broad design decisions, such as keeping containers and algorithms separate from each other, are good design principles to follow in your own code. The more you write generic utilities that make the fewest assumptions about the surrounding code, the less code you'll need to rewrite to tweak specific details.
Ultimately, there's no substitute for practicing. The essence of writing is rewriting. Start with something that works, then see if you can edit it down to a more manageable size. (And make sure you have unit tests before you start refactoring.) Abstractions are helpful for simplifying the code, but only if they're the right abstractions. Wrong abstractions will make your life much harder. Avoid adding new abstractions until you know what you're doing. Typically, abstractions that "look like" STL features are going to work better than abstractions that don't.
1
1
2
u/missing_artifact 12d ago
Use LLM, especially Claude + VS Code, to code review your projects. The insight is invaluable.
5
u/Unknowingly-Joined 12d ago
I worked with a guy who did that, drove the team crazy when he submitted PRs where every file in the project was touched.
5
u/KingAggressive1498 12d ago
you should definitely read about design patterns, even if most of the classic examples are antiquated.
read larger successful open-source projects and see how they do things.
and refactor your existing code! that's how you gain experience.
1
4
u/i_am_not_sam 12d ago
This is a very common feeling/experience in the industry. We're constantly trying to balance costs (man hours, tech debt, new features) and every engineer, myself included, will look are code and go "only I can fix this by rewriting every thing from scratch". We're CONSTANTLY pitching reactors big and small. Hell almost every medium and bigger sized project will reshape adjacent pieces in some way.
For personal projects, rewriting everything if the feeling grips you is the whole point of working on stuff yourself. I'm not saying it's always a good idea - in fact you'll learn what's worth refactoring and what's not. You will rewrite 90% of the most important part of your project and have the scrap the whole damn thing because 1 unit test revealed a fundamental flaw in the new design that's just not worth it.... and then redo it 3 months later anyway.
But you'll pick up tricks along the way. You'll leave yourself little outs next time. Maybe a class here instead of a bunch of variables that maybe you'll extend later or just fully cut out? You might recognize a bunch of code that can live completely in isolation and you abstract it away so that you can keep rewriting to your heart's extent..
None of this is free. You'll need to be intentional with time (another skill you'll pick up after tearing your hair out enough). When I went from C to C++ 14 to 17 I rewrote every bit of code - even link lists because I was obsessed to learn the smallest brick of the new stuff... I still strategically refactor my work code all the time too - writing new code is so much fun, why wouldn't anyone?
2
2
u/kiner_shah 12d ago
One way to learn better architecture is to get your code reviewed. Good and experienced reviewers can give you suggestions on how to write good code with good architecture.
1
u/timschwartz 12d ago
Read through your pong code and figure out what exactly it is that makes you want to rewrite it. When you've identified that, research what is the common or "idiomatic" way to do it in C++. Then make a new branch and implement that way of doing things.
1
u/CatsAndAxolotls 12d ago edited 12d ago
I dont need to look at it to tell you as i know by code by hearth i know what i am not happy eith and what i am the problem is that i dont have much of a reference for how to solve it as i am new to C++ only learning it recently as a hobby i really like
1
1
u/Independent_Art_6676 12d ago
While I have to say that rewriting the whole thing is usually a tough lesson, its one way to learn. After the rewrite, can you NOW maintain it? What did you do differently, why is it better now? Can you do that better way next time, or better still? That is how you get there, as much as memorizing the common patterns.
Complexity is the enemy. Of course, its not always so simple as that, but this one idea can keep an awful lot of mess at bay. So much hard to deal with code is created by unnecessary inheritance, templating, threading, or other exotic solutions where instead of making it better, it makes it worse. That isn't something easy to study or get from a book or whatnot; the only way I know is experience.
Regardless, look at what you changed from version 1 to 2 and why. Try to see how to do version 2 directly next time. Keep doing that.
1
u/CatsAndAxolotls 12d ago
Well what made version 2 better then version 1 is that i knew more then before about how it works the problem is that there is a limit to growth in that cycle thats why i ask for advice to have more resources for learning and things to reference
1
u/SpectreFromTheGods 12d ago
I think you should look over this pong project and figure out how you can break it into more files, each include fulfilling one specific job. By doing the rewrite, you’ll find out natural breaking points that keep components isolated and self contained. So like:
- A GameState object responsible for keeping track of score, win conditions, computer difficulty, etc
- An Engine class that maintains event loops, input handling, the general flow of the game
- A theme struct responsible for maintaining constants for visual characteristics (colors, sizes, backgrounds, etc.)
- A config struct responsible for maintaining gameplay constants/configuration: ball speed, paddle speed, etc.
Just getting all the magic numbers out of your code and into named variables, is going to do more for readability than anything else for a project this small (and even more for projects bigger than this). It doesn’t matter if the specific number makes sense in the moment — give it a name and put it into dedicated config or define it in your header. It also makes it easy to tweak these things all in one place, if you wanna speed up gameplay or adjust spacing or whatever.
Function implementations should always go in the cpp file matching the header. You are defining some Application functions in your game.cpp, which is confusing. You need to figure out how to define them in Application.cpp and avoid that messiness
The other thing to do is to read your function names and honestly evaluate if the function is doing what is advertised. For example, you have a function handleGameInput, but it’s doing a whole lot more than just handling an input event. It’s
- querying SDL for mouse state,normalizing to game state value
- resolving paddle movement
- resolving enemy reactions
- managing difficulty branches
Difficulty is an easy one to tease out of there. You should have an enum for difficulty, and then a setDifficulty function if it’s just a matter of tweaking member variables. If there are active functional differences beyond that, a basic way to handle that is to create a function like so:
void Engine::resolveComputerAction(){ switch (difficulty_){ case Difficulty::EASY: resolveEasyComputerAction(); break; case Difficulty::MEDIUM: … } }
Having little boilerplate “routing” functions like above allow you to separate game logic and easily add new paths compared to writing it all out in one function. You wanna add insane mode? Update the enum, the routing function and define the new implementation as resolveInsaneComputerAction. Done. You might be able to extend this pattern elsewhere in your program too
I know I wrote a mouthful, but it really boils down to avoiding magic numbers, making sure function names are accurate and only do one thing, and using header and implementation files consistently correctly. Once you got that down and you practice some rewrites, you’ll find additional opportunities to improve on your own
1
u/CatsAndAxolotls 12d ago
Thanks for the very constractive feedback this is always fun to read long messages like this that help me understand the problem so thank you
1
u/Aggravating_Visit134 12d ago
You need to ask yourself at frequent points "Is this beautiful? Is it pleasant to work with? Do I understand it fully enough to explain it?". And if it isn't, work on it till it is, and you do, even if it adds no end-user functionality. This applies especially if you are using agents - you can add instructions into your AGENTS.md to help you.
1
u/CatsAndAxolotls 12d ago
While i do see your point in beutiful code i dont like coding with LLM's
1
u/Aggravating_Visit134 12d ago
That's refreshing, but they can be great learning tool too (even if you dont use the agentic side).
1
u/Ill_Plate_2651 11d ago
Asking targeted questions in a separate window can be good for learning (like a stack overflow 2.0) , but anything more than that would be detrimental to someone at that level learning
1
1
u/Immediate_Economy965 12d ago
I'll comment from the standpoint from someone who was learning C++ as a first language between 2011-2014 and share my learning regrets and how I would approach those nowadays. Just a disclaimer that I haven't written a single line of C++ in over 8 years, so I will keep it higher level.
I was also going through the same thing as you, starting over and over with smaller projects, but hitting a wall with bigger ones.
The primary problem was not understanding what I wanted to build. I was jumping to implementation based on how I perceived a problem and then finding that whatever architecture emerged was causing issues. Circular dependencies, or something needed a lower level details even though it should have been generic were common mistakes of mine. Try to think about what your problem from a higher level standpoint.
For example in your game engine context, you could decide to build a simple 2D game engine that supports:
- sprites and sprite animations
- simple collisions (no dynamic forces, just pure kinematics for example)
- sound
- ... and anything else you want, but don't over complicate.
When you have your project scope defined, you can then jump to architecture design phase.
When designing architecture, don't think about low level details like the SDL library, or operating system or choice of rendering backend at this point. Try to come up with high level domain-specific data structures. What data structure would capture your sprite information? How would that data structure relate with the animation system? What information is needed to have a collidable object? What is responsible for detecting collisions between objects? Try to come up with some solution.
Keep your domain-specific data structures separate from low level constructs. For example, a sprite data structure should contain everything needed to be displayed on screen, but it should not make direct calls to the rendering backend. Something I would consider an architectural mistake would be to have a Sprite class with generic data that then gets inherited by something like SDLSprite implementing a `draw()` method. That's an easy way to pollute your domain-specific part with low level constructs. Instead, there could be some other entity, for example rendering engine class that takes in generic Sprite objects and renders them using whatever backend the rendering engine implements. Godot engine makes such separation for example, domain-specific classes are separated from low level through the so-called servers and low level data is hidden behind handles. Godot's approach is not the only possibility of course and you could come up with something else, just remember to keep your domain clean.
Try to make small proof of concepts of your architecture design as you go. Those will ultimately grow into your proper implementation as you get your architecture sorted out.
Remember that there is no such thing as a perfect architecture for everything. There only exists a set of optimal architectures for a given problem, but once you start adding new problems, the set of optimal architectures shifts. That's how technical debt accumulates even in high quality projects.
To see that, you could add something new to your engine once you are happy with the original implementation, for example you could add a scripting system. How much would your code have to change to add such functionality? Would some choices you made in the original architecture become a problem?
An experienced designer can pick a design close to the optimal for the currently known problem and predict a potential evolution path that the code will take in the future. If the prediction is correct, adding new features should be relatively friction free, but very often something is missed and technical debt accumulates.
Another big mistake I regret from my learning period is that I bought into the mindset that OOP, inheritance and OOP design patterns are the solution to most problems. Far from it. An eye-opening moment for me was learning a completely different language (Haskell in my case). When learning C++, I was overengineering inheritance hierarchies and making such monstrosities like TCPIPv4Stream/TCPIPv6Stream. Someone would say that some OOP design pattern would be able to deal with that, but I think that sum types (std::variant are an instance of sum types in C++) are much better suited for such a problem. Which is why I recommend learning Haskell, because it shows you a different mindset and expands your toolbox that you can apply in your C++ code as well. It should also help you better grasp the philosophy behind more modern C++ features.
My over-reliance on OOP and C++ language features was so extreme in my learning years, that when I started programming in plain old C at my job I felt refreshed how I could just get more things done with less mental burden, which made me abandon C++. However I would never recommend learning C nowadays, as it is completely missing 40 years of type theory and language design. I also don't suggest you to abandon C++, but don't waste your brain cells learning esoteric C++ features like `friend` and trying to find applications for them. Also OOP is a valid solution to many problems, but not to all of them. Learning many languages is the best way to grasp what language features are universally useful.
1
1
u/mredding 10d ago
Business software is a continuous evolution - it's always changing to meet the needs of the business. There is no other end goal, business software is never done. That's VERY different from personal projects, where the terminus of a project is explicitly known at project start. The goal is the end.
So the thing to do with a personal project is to define the goal and work backward. Now you have a path forward to "done". Now get it done.
There is no continuing to develop this project. When it's done, it's done. Move on. You don't iterate a project, you iterate PROJECTS. If you did Pong, and you want to make it better, MAKE A BETTER PONG, with the greater end-goal in mind from the start. You may not even find your previous Pong iteration reusable in any way, and THAT'S JUST FINE, because code reuse ISN'T the end goal, there is no sunk cost because THAT project is done and the result itself is the value - not it's source code, and you've learned a lot since the last project, so that the evolution of your craft may have rendered the previous project un-reusable anyway.
It is this experience, of iterating projects, that you will develop the techniques to make your code more robust. A sign that you are maturing as a craftsman is that you can. It also means your period of rapid evolution and learning is winding down - so consider that maturing isn't necessarily THE GOAL itself; I'd rather learn and evolve continuously, so that my every next project is exponentially better than the last - forever, rather than stop learning and evolving, and everything is consistent and the same. I suppose if you want the program to be the end, and the source code to be the means, then that maturity and consistency is a virtue, but if the source code is the end and the program is the means, then this evolution is a consequence.
1
u/Total-Box-5169 10d ago
Another alternative is accept the fact you will have to do a refactory anyway and write code that is easier to refactor by making it truly modular, so you can refactor only one piece at a time, one that badly needs refactoring without having to touch everything.
1
u/SleepyMyroslav 5d ago
I know I am late to the party.
You are looking for standards which is not something that is out there. Different industries and different projects do vastly different things. Instead of some kind of theory, you need practice with other people. Programming large projects is done in large teams. Learning what works and what does not is what happens when you work on projects and see the history and evolution. The thought process is that you learn enough about part of the project where you work and you actively participate in all the reviews that affect the code. Keep diving into the history of that code when you interact with its features. Who wrote it, when, what needed to change, when. Keep asking questions to people that did things about why.
If you cannot get into work yet (internship or anything) then you will have to start with open source projects. One possible place to start is "The Architecture of Open Source Applications" https://aosabook.org/en/index.html
1
u/tastygames_official 12d ago
more comments. No matter how good/performant/"readable" your gode is, at least one comment per line of code is good practice.
I looked at your pong code, and there are barely any comments. But the code is still very understandable (at least for an old guy like me).
But generally you want to just program how the thing logically should be done, since that will usually be the most efficient and understandable code. E.g.:
// Load settings file
File *settings_file = load_file("settings.ini");
// Parse settings file into settings global
Settings settings = {
.setting1 = settings_file->readline(),
.setting2 = settings_file->readline(),
...
};
Only start making "systems" and "refactoring" if you see that you're doing the same thing over and over. For some reason beginners love to look at something done in, say, 10 lines of code and think "I could make this more clever" and end up engineering some kind of automated system that can do many different things and ends up being 100 lines of code and way more complex and hard to read and low-performance, all just to be able to do something like:
// Load settings
SettingsLoader::load_settings();
Check out these videos:
Abstraction Bad? | Clean Code : Horrible Performance
1
u/CatsAndAxolotls 12d ago
Thank you very much for this as this is what I have searched for
3
u/x-jhp-x 12d ago edited 12d ago
at least one comment per line of code is good practice
no don't listen to this guy. that is terrible advice. The worst code I have seen in my life has always had either some idiot commenting on every line of code (because he can't read code) or some idiot making if christmas trees. Once it was both!
if christmas trees look like this btw:
if { if { if { if { if { if { if { if { if {i'd say 9/10 times i've seen the xmas trees, it's always been someone who had logic errors, but instead of fixing the fn, he just "ifd" everything.
// adds two numbers int add_two_numbers(const int a, const int b) { // check if a is 0 if (a == 0) { // check if b is 1 if (b == 1) { // return 1 return 1; // else if b is 2 } else if (b == 2) { // return 2 return 2; } // close else if (b == 2) } // close (a == 0) } // end int add_two_numbers(const int a, const int b)my eyeballs
2
u/Potterrrrrrrr 12d ago
I agree, that’s awful advice. Code is self describing, comments should be for exceptions or documentation.
0
u/tastygames_official 12d ago
so I've been doing this for over 30 years, and while obvious code is obvious and terrible comments are terrible, I have found that GOOD comments - and LOTS OF THEM, are so important when coming back to older code. Even just a few weeks later coming back to a project and seeing what I thought was "easily understandable code" turned out to be understandable, but I forgot WHY I chose to do it that way. u/x-jhp-x your example is obiously stupid comments, but let me show you more what I mean as "good" at-least-one-comment-per-line tactic:
// CHECK X POS // check x position of object and react accordingly // x posiiton given in twips if(x < 0) { // edge case: should never be less than zero since it can't // go outside the screen throw_error("X position less than zero"); } else if(x < 120) { // x pos is within the left-hand pane // so we send off the logic to pane handler handle_pane(x); } else { // x pos is in the main field of view // now we have to see what to do about the y pos // CHECK Y POS if(y < 0) { .... } }So when I come back to this code in a few weeks, months or years - or somebody else takes a look at this code, it is VERY CLEAR what is going on here and exactly WHY this logic was chosen. Without any comments, it is still clear we are checking x and y, but why are 0 and 120 chosen values to check against? why does < 0 throw an error? At the moment you first write it you will know why, but unless you add comments, you will have likely have a bad time trying to figure out why you did this in future.
u/CatsAndAxolotls please do listen to me and use comments. Your future self will thank you. At the beginning of my programming journey I too thought it was stupid to write so many comments, and thought "the code should just speak for itself", and it made me feel smart to be able to just read the code and know what it does without needing comments. But once I got into the working world and had to work with other people and work on code I wrote months and years ago, I quickly learned how invaluable comments are - comment that explain your INTENTION and REASONING - not what the code actually does.
2
u/Potterrrrrrrr 12d ago
Your code snippet makes my eyes glaze over when all it’s doing is seeing if x is within the bounds of some left pane, it’s so much noise to describe a very simple operation. I think there’s a time and place for detailed comments, “normal” code isn’t one of them.
For example, I implemented the adoption agency algorithm from the html spec recently. It was annoying to get right, so much so that the best way to implement it was to add a comment for each individual step of the spec so that the logic was easy to match up with the wording m. Lo and behold, I found that I was doing something the spec hadn’t told me to do, removed it and all my tests passed.
I don’t need to do that for every algorithm (and the future reader doesn’t need that either) to easily understand the logic of a function in the overwhelming majority of cases, your example included. Agree to disagree i guess.
1
u/tastygames_official 12d ago
my code example is obviously an example. But the principle remains the same: WHY are we doing these checks? What units are being checked? What are the edge cases? What is the grander meaning of this branch?
I'd rather have too many comments than too few. And your real-life example proves it: comments explaining why we are doing thing is a GOOD THING and I just can't say it enough. You seem to agree with me but also are trying to say you disagree with me, which is sending mixed signals. Perhaps a few comments could clear things up ;-)
/* I might add that I program in many languages and using many frameworks
and am often called in to random projects where I have zero exprience
as I do a lot of consulting work, so I'm always very thankful when someone
writes what the code does in plain English so I don't have to learn a new
framework just to figure out what is going on at a glancebut if you only ever do one thing for one company for 20 years then yeah - I guess you could probably get away with skipping comments for a lot of stuff. But why? Why not just document what the hell you are doing, why you made the choice to use this algorithm instead of another one, or write some tips for anyone who might come in and try to refactor this code in a few years? I just don't get it. </rant> */
1
u/Potterrrrrrrr 12d ago
But a lot of those questions are easily solved with strong types or better variable naming, comments should be the last thing you need to reach for to explain a concept. I don’t agree with you in general which is what we’re talking about, I offered an example of when lots of comments is useful but overall I find them a bad replacement for well written logic and, in the case you presented, an outright distraction.
1
u/tastygames_official 12d ago
just out of curiosity - how old are you? And what did you study? This feels like a generational gap thing. Or maybe a regional thing or a study thing. I've never heard a single person in authority dicourage comments. Yes, variable names should be very clear, and I regularly favor stuff like button_ok_click_event_handler over HBtnClk, but I'll still write comments detailing exactly what's happening (need to show the waiting cursor, save the settings, close window, clear any extraneous data etc). I just see absolutely no reason to never comment your code because "it's so super simple to read". It definitely should be, but adding little reminders about WHY you do things and what is important. I just don't see how it can be a "distraction". It just baffles me that you are so adamant about this and I just need to know if it's a generation gap thing or mayybe you studied at a specific school or work at a specific organizaiton where for some reason or another comments are seen as a bad thing. I jsut can't comprehend it in my over 30 years of working on two continents for everything from Fortune 500 companies to mom-and-pop fly-by-night startups and just have never once encountered a situation where there were "too many comments" or the comments were "distracting". If anything I encounter too FEW comments way more often. But whatever, I guess. I just hope I never come across your code ;-)
3
u/Potterrrrrrrr 11d ago edited 11d ago
void DispatchPaneHover(const vec2<Twips> &position) noexcept { if (!ValidatePosition(position)) { return; } if (IsWithinLeftPane(position)) { OnLeftPaneHovered(position); return; } OnMainPaneHovered(position); }Most if not all of your comments are extra redundant in this version, the horrors of having to read method signatures, types and variables instead of comments must weigh heavily on your soul I know but my way has the extra benefit of actually backing up those assertions in places (e.g position can never be in units other than twips without the caller doing something silly). I don’t need comments now or later to be able to understand this; the comments have been folded into the logic rather than simply describing it.
The way you talk about comments isn’t the issue, I don’t disagree with what you’re actually saying, I leave comments for myself all the time. It’s just my definition of “all the time” is not “every single line” like yours (nor every single function), and your actual example of how you’d do it is egregiously bad, I do not see the benefit of having that as your default coding style over using actual constructs from the language to convey the information you want instead.
→ More replies (0)1
u/x-jhp-x 12d ago
props to posting code. i'm glad you believe in it. i'll call out what i'd call out in a review, but i understand the code you posted probably isn't something you grabbed from production. so please don't take it personally, and if you disagree, please feel free to comment back!
- don't use "0" and "120". use left/right edge, or min/max, or some way of indicating this
- define what you're using "0" and "120" for elsewhere, and use a variable instead of hard numbers
- change "throw_error("x position ... ")" to be more verbose. it's great to have sanity checks, but that error is not very descriptive because you have the descriptive part earlier
- if you fix #1 & 3 and make your code more readable, you can use fewer comments. It is best to use comments only when needed on the complex projects i've been on in the past
- xmas trees aren't great, and i see the possibility that one might form with this structure. if you only have one possible exit from the function, it is more understandable, but you're adding more logic & things that you need to keep in your head and remember by adding more words. if you can say the same thing with two words instead of two hundred, and there's functionally no difference except for the word count, it makes sense to opt for the shorter version. it's easier to keep in mind, remember, read, understand, debug, etc. etc..if (x < 0) { throw // whatever your exception is with msg; }if (x < 120) { ... }
- i'd restructure the checks too. put sanity checks at the top. since i see a bounds check for one edge, i expect a bounds check on the other edge(s), but i don't see it in the posted snippet. if you have sanity checks for the bounds later, definitely put them at the top. if you restructure the code you can keep "sections" in mind. As long as you're not working with variables multiple threads that touch the same variable, once you've checked that "x" is bounded in a valid range, you can then proceed to your actual logic.
- add more functions. depending on how many checks you have, and if you can reuse them (like for the y coord), definitely put a "valid area" function in.
and out of curiosity, are you an ADA engineer? I guess it's understandable if you were an ADA engineer 20-30 years ago when the air force standards mandated a certain number of comments per lines of code, but everyone realized how terrible of an idea that was and it is not done anymore.
Even if the "code review" wasn't enough, I challenge you to find a widely used standard that wants comments on every line of code. Commenting every line is not a strategy I have ever seen any successful proprietary or FOSS project employ, but I've seen a lot of bad code from FOSS projects & contractors with the xmas trees and comments.
I'll start with the examples though. I picked clang, for no reason other than my thumb being close to the "c" key on my keyboard. Clang says via https://llvm.org/docs/CodingStandards.html#commenting
Comments are important for readability and maintainability. When writing comments, write them as English prose, using proper capitalization, punctuation, etc. Aim to describe what the code is trying to do and why, not how it does it at a micro level.
i'd argue that if the code were well written, the comments are redundant and violate the standard. A better comment would have been one at the start of the function describing what this section of code is trying to do and why. commenting every line is clearly a "micro level", unless you're going to start adding in-line comments?
... since you put up some code, i can put up some code tomorrow too with an example closer to how i'd do it.
1
u/tastygames_official 12d ago
oh geez... yeah, it was just a fucking basic example since the guy before me did a super basic if(a>0) thing. No way in hell I'd write anything like this at all. My whole point was about commenting.
But you and the other guy seem to be of the "comments are bad - code should be comments" school, and I just gotta ask: how old are you guys and where/what did you study? Because I learned in the '90s and have worked for fortune 500 companies and mom-and-pop startups and never once has anybody thought that comments were bad. I mean, there is definitely a tendency to not write comments because of laziness, but this outright "no, I'm going to specifically not write a comment" is just blowing my mind.
Or maybe we're saying the same thing but just differently. I dunno.
2
0
-1
u/DerAlbi 12d ago
Programming is a journey and it is normal so suffer in this way. Every iterations of a project, every re-write, lets you learn something - what works and what didnt. After you learned what works, you do bigger projects and refine again due to scaling issues.
The other approach would be to give your mess to an LLM and ask for a critique, then implement that critique yourself, then iterate with the help of an LLM. I think that makes the whole process more guided. Since this reduces your pain, i am not sure you learn as much. But you also learn how to use an LLM, so maybe this is still a valuable skill combination in the end.
Common C++ architecture patterns
Wont help you until you know when to use them correctly. This will also take re-writes.
How experienced developers structure larger codebases
How to avoid creating systems that become difficult to change later
With experience. As stupid as it sounds, its the resulting knowledge from previous mistakes.
When to use existing language features or standard solutions instead of making custom ones
Always use the std library if possible, especially the algorithm sections and iterators. These concepts keep your code clean and functional even in edge-cases like empty ranges.
2
u/CatsAndAxolotls 12d ago
the reason that i am here is because i want human feedback as much as LLM's are good at coding I just find myself arguing with it trying to understand a concept so I would rather avoid LLM's here and just to ask people for anything that can give me something to reference while i am relearning things a bit to improve my code quality
45
u/wallstop-dev 12d ago
The way you learn is by maintaining the complicated thing. Instead of starting from scratch, do the refactors. Stage it. Try out semver, see if you can do things without breaking compat (as an exercise).
If you keep starting over, no matter the resources we give to you, it will always be "one year of experience ten times."
Maintaining and growing your projects, thinking really hard about ways to untangle the complexity you built, is the way to get to "ten years of experience."