in the course of my work, i feel as though i've gotten quite good at "triage + fix a reproducible bug" but still suck big booty when it comes to "design a brand-new system". any advice for how to improve in this area?

🏳️⚧️ and I make good posts sometimes!
pfp by me!
banner by https://twitter.com/spect_ion
in the course of my work, i feel as though i've gotten quite good at "triage + fix a reproducible bug" but still suck big booty when it comes to "design a brand-new system". any advice for how to improve in this area?
hm, this sounds like you are struggling in the "large" aspect of software?
if so, there are a few resources on software architecture that are pretty good!
and while software "architecture" can be quite neboulous and sometimes even make people run away, i think it's actually really useful and, uh, well, real 😅
one approach that i think is still baller, is domain driven design. it's not the be-all end all, but i actually really like it! One criticism of this is the sort of "object oriented nature" it has, but there are adaptations of it for FP, like Scott Wlashins "Domain Modeling Made Functional", which i enjoyed!
Lots of age-old advice and writing on architecture by martin fowler holds up incredibly well, and if you haven't, check out his architecture guide, it's really good: https://www.martinfowler.com/architecture/
there's so much more but that's i think a good first recommend and starting point
hm, this sounds like you are struggling in the "large" aspect of software?
yes exactly! thanks for the resources, i will take a look
are you trying to implement systems you've designed and found them lacking once implemented? or is the problem earlier than that? when embarking on a design for a system, do you codify its aims and stick to them, or try to feel it out?
it will sound reductive, but it really is best to separate what the system needs to do from what you would like it to do, and to periodically check those lists against each other. until a system does what it needs to do, anything extra just increases the cost into something that doesn't work - make it work, then make it work well, then make it work fast!
I think I have trouble going from "list of requirements" to "system that implements the requirements effectively". To be more specific: I tend to avoid "big picture" views and instead head directly towards the nitty gritty, feeling out what the shape should be by seeing how naturally the code writes itself. This has led to some issues: when integrating with a larger system, I've tended towards designs that overly conform to existing practice, instead of identifying a more meaningful way of expressing things. Or, I get too caught up in my original idea and re-evaluate too late only once a lot of code has been written already.
There might be a step of "plan out what you're going to do" that I'm missing, but I'm not sure how such a step would help me currently, or what such a step would even look like, because of the way I've been working.
I'd suggest trying to catch yourself before heading to the nitty gritty, then - ask yourself, when implementing some detail, if that detail is really important, and what assumptions you're making upon which that detail depends. if you're familiar with sketching, it's the same thing; there's no point trying to shade a character's hands if their limbs are in the wrong place! define constraints early, and leave details until the last possible second to decide, otherwise you might end up closing off design options without knowing it.
basically, implement stuff breadth-first rather than depth-first, and periodically take a step back and compare what you're making against the list of requirements you have to make it with.
this is all gonna be a ramble but I think one of the easiest ways I get a feel for how to structure out systems from scratch is to take a look at how similar implementations of said systems work. this, and hoovering up ideas on general software constructs (e.g. different kinds of memory allocators, threading models, etc).
i think you can get a lot out of brainstorming sub-domain problems out of that given system that certain algorithms/patterns would ace. or, if you come from a debugging / triaging strength, consider why different algos/patterns would flounder, or how some existing system sucks, and approach from an angle of improvement/refactoring.
even if you don't do any sort of games programming i give a strong reccie to both https://craftinginterpreters.com/ and http://gameprogrammingpatterns.com/ for inspiration for considering the more concrete considerations of breaking down complicated systems into more manageable chunks
hoovering up ideas on general software constructs (e.g. different kinds of memory allocators, threading models, etc)
i like to think i've done a lot of this but maybe not for the kind of systems i end up writing (Business systems)!
take a look at how similar implementations of said systems work
so yes i do need more experience here. i learn a lot from reading my work's codebase but i'm not sure if i've internalized that knowledge after i've moved on from a problem? or what that knowledge should look like if i'm messing up the generalization step
how some existing system sucks, and approach from an angle of improvement/refactoring.
i will try to do this and think more about how the overall design could be misguided & caused a certain bug to be likely rather than lasering on that specific bug. good exercise
strong reccie to both https://craftinginterpreters.com/ and http://gameprogrammingpatterns.com/
i've heard good things about Crafting Interpreters before, GPP also looks nice (and non-engine game software is Business-like in many regards) so thanks for the good recs!!