So I have been coding for many years in highly level languages and I never felt my code unmaintainable.
I’ve been playing around with zig for over a year… and everytime I start a side project, as my code grows larger, I find my code unmaintainable… I drop my zig projects after like implementing like 20-30% of the Idea, whereas in Go, I can push it 70-80% before I loose interest. (not accurate stats, more like how I feel. also I almost never use anything outside std lib).
I can point out several reasons but I think it’s mainly that I *almost*** never coded professionally so I never had to worry about the maintainability.
If you are dropping interest regardless of what the language is then maybe this is an area you should look into first.
Writing maintanable code and having interest in what you are doing go pretty hand-in-hand.
There is plenty of information about maintanable code on the internet. My biggest would simply be- in no particular order: good folder structure, good names, decent comments.
It’s a complex subject. Experience definitely helps, if you try to tackle hard problems that are close to the limits of your abilities (which is not necessarily a bad idea, that’s how you push the line forward), it makes sense that you get to a point where you’re stuck.
Dialing the difficulty down a bit might help (i.e. try an easier project), but alternatively a good approach is to be willing to restart the project, meaning that you just re-do it from scratch a few times, each time incorporating anything new that you learned along the way.
One major cause for getting your project into an unmaintainable state is accumulating too much debt as you try to sprint towards getting the happy path of a feature working. Redoing the work from scratch helps both with debt (since you start from 0 again) and forces you to slow down.
If that is the issue that you’re facing, over time you will learn what is the right pace for you and, most importantly, when to speed up and when to slow down based on your perception of the state of the project.
Some of the concern may be here. High level languages hide a lot of complexity from you. You don’t have to worry about memory management, pointers vs values, etc. Some of what you are feeling is the extra cognitive load of worrying about aspects of the program that you didn’t before.
I’ve been running into this a bit with my side projects. I get a bit paralized worrying about struct size or how to do it efficiently before I even know the shape of the data or the way it will work.
The opposite also makes it hard. You throw everything into hash maps and try to ignore allocations which ends up with a program that is hard to reason about.
All this to say, I struggle with this too, and I’m trying to work on it by not being afraid to rewrite it and trying to identify when to care about specific aspects (memory management, struct size, etc.)
At least for me this is the best approach. I program nearly every at least twice. And really just delete the old code before starting the next one. For some projects I’m on my 5th time but it’s getting really good.
I’ve been coding for about 7 years and about ~6 months of professionally as intern. In those 7 years i have done max 2-3 serious projects, rest just messing round and tinkering things. But recent times I feel some sort of burn out even for tinkering things and I’ve been soul searching.
that’s what I usually do, tinker with things beyond abilities after I get something working, I move on to other things.
I’ve heard many people talking about restarting project but I have never done. Sometimes I randomly open on working projects I started 5 years. But I’ll try that, I’ll try restarting my older projects.
I’m do understand the concepts but yes manually memory management has forced me rethink how I code and also resulted in messy code. also mostly I code alone so I never worried about whether other people can understand it so I get away with writing confusing code.
There’s a couple of Go Proverbs that I’m always reminded of, even when writing Zig:
A little copying is better than a little dependency.
This one is the big one. Especially if you’re doing something new and learning as you go along, writing maintainable code in the sense that it’s well-factored and “clean” might just be impossible. Over-abstraction is the bane of my existence as well and sometimes I just need to get over myself, copy an implementation from somewhere else in my code base, change what needs to be changed and worry about cleaning it up later (if it even comes up).
The bigger the interface, the weaker the abstraction.
Similarly related to over-abstraction and over-thinking. If you eschew the need to do these things and just focus on writing code that does the thing you want it to, the interface and abstraction will follow through necessity as you continue to evolve the codebase.
The final thing that I can really add from my own personal experience, not related to Go, is write tests. If you’re on a big project that’s hard to really see benefits from until all the individual components are complete, writing tests - unit tests, e2e tests for the functionality, whatever - will give you tangible feedback that you can then build on. From my experience, once I know one component works as I expect it to, it’s all that easier for me to have confidence that the rest of the project will fall into place.
Not sure how much online programming discourse has affected you, but so much of what I see online is stuff like “memory management is easy! just understand the lifecycle of all your objects and use arenas!” and other such pronouncements that seem intent on making (often less experienced) readers feel like dummies. The reality is that, while you’re still trying to understand what a good solution looks like, there’s nothing wrong with (for example) throwing stuff into hashmaps and cloning freely. To Loris’ point, any sufficiently hard problem is going to warrant a rewrite or two before you understand the problem intuitively enough to do all the “right” things.