Ask HN: What boosted your confidence as a new programmer?

211 points by optbuild ↗ HN
When anyone starts out in a new craft, even after grasping the fundamentals of the tools they are quite shaky and low on confidence until they have a significant experience in doing something that ultimately ramps up their confidence in their craftsmanship.

Similar things happen with novice programmers when they start out.

Then they read a beautiful codebase which they can fully understand and replicate, or build a project from scratch, or read a book or take a class on a subject. And their confidence is tremendously boosted thereafter.

What was it for you? How did you gain this confidence to take the first step from being a tinkerer to being a skilled craftman?

244 comments

[ 3.0 ms ] story [ 301 ms ] thread
My first programming language is JavaScript back when it only lived in the browser. The compile target of the browser is, and always has been, the DOM. The DOM is a tree model. When I learned to appreciate that in my first year of programming everything became simple. Even before JavaScript became fast you could do most things with relatively high performance easily cross browser once you achieved that solid foundation. No frameworks, no unnecessary abstraction libraries for vanity, no bullshit.
Where did you learn to see it like that?
I am self taught. I was involuntarily reassigned from a design to a developer position and just had to learn to program.
It was over 12 years ago now... but before then, I'd only taken up programming as a hobby. I was addicted to gaming and computers during my high school years, either playing a game or coding something.

Went away to college for something completely different, psychology, but when I got home and started applying for jobs, nobody would hire someone with a bachelors in science only.. so I decided to pull out an old trick of mine: I would apply to the programming companies too. Got an interview. Got the job. Even though my math skills were horrendous.

The confidence in my programming skills came a few months after they hired me. Til then, I'd been working on building various little programs that had no real purpose. A ping pong came, a slot machine, etc. My tyrant boss had me go through training to use various functions in Visual Basic 6. Some of the most valuable ones were using arrays and collections, which had helped me understand databases later on when I was learning PHP.

I was developing software for an autobody shop. I really couldn't wait to work "in the main program" but once it was established that I was ready, that's when the confidence started taking off.

About 6 months later, his customer base went from a few hundred to a few thousand. My job was to focus on the UI, making it more user-friendly and easier to use and fix existing bugs in the program. I loved and hated that time of my life because it led me to meet a wonderful woman, yet I was going through hell everyday for that tyrant. Fortunately, I took what he taught me and used it to further establish my career elsewhere.

The confidence really came when I deployed my first few medium-to-large features to production. Positive user feedback, few errors in production and good measured performance made me proud of my work, that’s when I realized that I’m going to be just fine!
My first full stack app. Didn't need to be complex, but it worked.
Realizing that external dependencies are regular codebases just like the one you're working on. That you can open them up in VSCode, look around and figure out any bugs or issues you're having and even open pull requests to improve them.

At that point, you lose the feeling that there are magic things out there that you will never understand and that for the most part everything is just regular old code that regular people wrote.

Adding to this, the decompiler built in to many ides really up'd my game understanding underlying libs. How they work, what methods to call, etc. Very helpful! As much as people trash java this is a really nice feature. I'm sure other languages have decompilers as well, but I've never seen anything close for c# for example.
>I've never seen anything close for c# for example

If you highlight a method you call from external code and hit CTRL + F12, Visual Studio will automatically decompile it for you.

>I'm sure other languages have decompilers as well, but I've never seen anything close for c# for example.

Dotpeek is integrated into Rider and is a world class decompiler. It also integrates into Visual Studio either standalone, or with ReSharper.

You can also integrate external source symbol servers into your IDE of choice as well that will let you debug into libraries seamlessly.

In python pdb ‘breakpoint()’ is great
In the case of Java at least, what helps is that the IDE can decompile the code or, which is often even more helpful, download the source code and allow you to step through it while debugging, at least if a source JAR was published (which is pretty often the case).

In the case of non-compiled languages, of course you don't even need this step since all your libs exist in source form already, so it was pretty simple for me to step through Ruby library code with a simple debugger and no fancy IDE.

I have a habit of sometimes debugging even horribly abstract framework (e.g. Spring) code when I don't understand what it's doing. That's maybe not the most efficient method, but it does usually make me understand why thing X is not working the way I expected it to work.

This is indeed a superpower.

I don't really remember when I felt that external dependencies were magic, but thinking about this, it explains a lot of the behavior I see on some developers who are very negative about the more challenging parts of the job.

Some of them don't really believe the research stuff we do at work are even possible. They're constantly surprised when other devs finish those tasks. Some don't believe that other devs can code in C++ or Rust, or write parsers, database modules, implement IQueryable in C#, or develop novel algorithms for novel applications.

To them, if a package exists it must just work, and that package comes from another breed of developer that can't coexist with them. I see a similar thinking with AI: now with ChatGPT and GPT-4, there's a hubbub about there being "no reason for our AI team to exist anymore".

I'm not a big fan of working with those developers.

> I'm not a big fan of working with those developers

I agree. And it ties into something I often see that puts me on edge: programmers not taking responsibility for the code they put into their projects.

What I mean is that when you incorporate any code, from any source (library, framework, copypaste, etc), then you are responsible for that code and its proper behavior as much as for the code you actually wrote. So you're well-advised to understand it.

That's one of the reasons why I won't include code that I don't have the source code to. I need to understand it and be able to fix it.

Good point.

The "out of sight, out of mind" approach doesn't really work for code you're actually responsible for.

I've worked with some of those folks too. It seems to me like they haven't really learned programming the way I understand it; instead, they've learned various incantations that can be strung together, and are just at a loss when they don't work as expected/documented.
Oh, that's definitely true.

To me the issue is they assuming everyone around them is like this. I'm totally fine with lack of experience or knowledge, but a co-worker constantly underestimating their peers is not alright.

The senior developer above me is like this for me, and it’s rough because it shakes my self esteem AND often leads to me having to support and extend a fragile and changing 3rd party library to solve a rather specific problem that would be better solved with custom code.

Where it really stings is when I do something of my own initiative (like, for example, create a transparent API over memoizing and caching some expensive calculation, or refactor some of our common client customizations into their own set of classes so it’s easier to extend) and he ignores it or scoffs at it.

Only to then find a third party library that implements something similarly. THEN it’s presented to me as a “brilliant idea” that we can take advantage of.

At that point, when I say “we” already do this, he usually rephrases what his brilliant 3rd party library does, as though he can’t fathom that I would be capable of doing something like that myself, and clearly I’m just not understanding what he’s telling me.

I think it’s a defensive mechanism for his ego against one of his peers or employees being more capable than he is.

But it’s also not like he can’t learn this stuff himself, he just doesn’t ever put in the time or effort.

With many exceptions (the left-pad debacle comes to mind), it’s generally much better to use a third party library instead of supporting your own implementation.
I know this is the mantra, but my experience has been highly mixed and only generalizable to how low in the stack it sits.

For, say, security implementations for authorization and access controls, or even low level HTTP request routing? Absolutely. The goal there is to adhere to something standard and battle tested by experts, and the third party libraries tend to be fewer in number, and of higher quality, with longer term support and clearly defined upgrade paths.

But that’s the lower level stuff, where your special custom needs are superseded by the primary goal of just “doing the one right thing”, or “adhere to the commonly agreed upon standard”.

For all the other things that make an app unique - things like CSS frameworks, UI components (beyond basic, accessibility-minded building blocks), chart drawing, report generation and caching - my experience has taught me otherwise, the hard way.

Being stuck using a 3rd party library that doesn’t do what the client or business needs it to do, having to juggle our own internal patches and bug fixes with updates to the library itself, all only to have the library abandoned or deprecated in favor of the author’s next pet project, really sucks and often comes with a high opportunity cost and a high development cost.

I now consider third party implementations of higher level features (and especially anything front-end) to be something that needs to be evaluated as equally costly as an internal implementation by default, and not favored just because somebody else wrote it.

Maybe I’ve just been unlucky in my experience, though. I also suspect ecosystem makes a difference. The PHP and JS ecosystems are full of poor libraries with snake oil sales pitches. I suspect this is different with, say, Rust.

I think we mostly agree with each other. As I said, there’s many exceptions.

I’ve mostly worked with Python and JVM languages, which probably explains why I’m less passionate about the counter-argument than you are. Ecosystem definitely matters a lot. VanillaJS is the only good JS framework IMO.

I’ve noticed this as well - they don’t view software building as a form of engineering that can be learned from basic principles of computer science, but instead as magic.

So the go to for every solution is to find a third party library from a “real” witch or wizard, and follow its basic tutorial. Maybe try to customize it a bit at most.

If something breaks, just start randomly moving things around or copying and pasting more code from forums until it works.

I can’t live like that. I need to know why something’s not working, AND why it IS working. I like stepping through my code with a debugger just to make sure things look right, even when they’re working.

I think the craziest part, though, is just how much people with this “software is magic” mindset can actually get just by brute force cobbling things together.

I’ve never met a developer with that attitude, but I’ve met too many managers with it. You’ll be even less of a fan of working for those developers, I guarantee it.

That mediocre dev is a straight shooter with middle management written all over them…

I think it depends on the industry and in location. In my neck of the woods I'm seeing an uptrend towards more technical managers, or sometimes a mix of lead developer and manager, who can actually get things done.

It's a matter of preference but IMO/IME it works better when a manager is actually good at it.

A manager that is supposed to be technical but only wants/knows how to do the management part, and can't make more than the basic stuff won't really fly. I've seen a couple of those getting fired during probation period.

On the other hand, the "bad developer" that thinks packages are magical will eventually settle as an expert beginner in a low-expectation environment. Which is fine.

Funny thing was I never even thought to do this until I was working on a very strange bug, and a senior engineer at my company suggested I look at the source code for one of our dependencies. Sometimes really obvious and basic advice can be a big step for people.
Yeah I think it's helpful to recognize that this isn't always obvious to people, even folks who seem like they'd instinctively do so–I had a similar experience a year or two into being a professional programmer, despite being someone whose first experience with dependencies years beforehand was downloading Perl files and directly editing them.
Had a similar experience during my Bachelors thesis. I was adding new functionality to an existing code inspection framework and was also supposed to add a graphical interface with GTK (this was 2014). At some point I identified a performance bottleneck within a GTK component. My advisor suggested fixing it and I just couldn't understand how I lowly student am supposed to tackle anything in this big behemoth. In the end, I didn't do it but it made me think and jump into various big open source projects in the following years. And you get used to navigating these suprisingly fast.
Whenever I have this situation, it's always with a library too big for my smooth little brain to comprehend.
The trick is to dig deeper into those big library's dependencies as well. It's turtles all the way down.

The other thing I find is that big libraries are either mostly dependency bloat (as implied above) or dealing with a hard domain problem. If it's the latter, what you're really struggling with is not the library, but the domain it's trying to represent.

"If it's the latter, what you're really struggling with is not the library, but the domain it's trying to represent."

But this doesn't change anything of the programmers problem. If I stumble on a bug in a physics libary I am using for a game, I cannot just jump right into there and go fixing things. I mean I can start doing it, but at the cost of not getting anything else done for quite some time.

There are lots of hard domains in programming. Cryptography is hard. Networking is hard. Fast rendering is hard. Efficient DBs are hard. OS are hard. Drivers are hard.

You can maybe fix a trivial error in such libaries, but everything else is usually a (big) project on its own.

Absolutely correct, but I think it’s still valuable to be able to recognize if you’re struggling against the code or the domain.

Additionally, once you recognize that you can also recognize if you’re dealing with incidental complexity (e.g. poorly thought out/designed code) and inherent complexity (e.g. physics calculations). The former can be fixed, the latter cannot. Knowing the difference saves much pain.

Relatedly, understand the frameworks that you build upon.

For React devs, this means learning how React actually works under the hood.

My favourite language for this is Go. The standard library is totally exposed and easy to jump into, right there for you to learn from and to make sense not only of the library but how to actually write Go in the first place.
CGO_ENABLED=0 for new projects though :)
Not all external dependencies are noncompiled. Also, library code is often different from your regular codebase especially in a language like C.
Just calling out that if it weren’t for open source this would be much harder
Imagine any other profession needing to inspect, rebuild, and fix the tools that they are given to do their job.
that's actually, um...common? i just had to repair an electric jackhammer last week. i worked in a machine shop for a large well drilling company not long ago, and not only did we create/repair tools for the company, but obviously had to keep our mills and lathes and cranes, etc. in good working condition.
It's not common in the same sense. First of all, tools are very different from software products. And there is never the same level of analogy that one has to do in software.

Imagine buying a hammer, that hammer not working, the hammer's design being so complicated that it's impossible to understand or mend, and then having to design and build your own hammer, and then putting up with that situation over and over again and accepting that as the status quo. That would be the correct analogy.

I'll agree wholeheartedly that the analogy needs some work. Tools are different - we have our literal physical tools that we don't generally dive into (keyboards, mice), we have tools that are maybe more battle tested and rarely examined (cat, grep, find).

We have do have tools like the hammer - there is one design, everyone more or less agrees on it. There is still high quality and low quality, but it has one job. We have tools like a bulldozer - complex, numerous parts, requires constant maintenance, closed source.

As the parent said - it is not uncommon to have to maintain old equipment, as well as design new tools as new requirements pop up.

Sure, our rust is a little bit different - time wears on software in a different way. Use wears on software differently. (Changing product requirements leading to a new tool is probably common.)

The maintenance may be trickier - but I'm sure changing components on a tool when a certain component is no longer available is not easy, thats where shim layer comes from!

I feel like this is quite common but it depends on the optics of situation.

A mine site usually fixes its own tools used to do their job, they usually fixed things they've purchased to use that are broken or become broken, is that not the same?

I highly doubt that a mining operation finds it acceptable that tools that they have purchased show up broken, undocumented, and unsupported.
You'd be surprised how common it is.
I've worked in many industries, and none of them put up with the poor state of tools and treat it as a given like the software industry does.
Hmm again I think mining are pretty good at making do with the poor tools they have

Very different situation

I've also found that the software industry obsesses over tools more than any other industry

Your job as a developer is to write code and piece code together to make your life easier. That includes taking the work that others have done and make it work to suit your code base.

On a mine site, the job is to mine and process minerals. That includes engineering a mine using products that eventually fail over time and need fixing. As part of that process an automotive electrician may need to fix the electronics on a vehicle that have become faulty or damaged.

Mechanics and engineers need to inspect, rebuild, and fix the tools they are given to do their job. The difference is that yours may not work up front, whereas these tools need maintenance over time.

When I was doing enterprise work with closed-source libraries, any defect required them fixing it. We've found some issues with AWS and they fixed it too.

The difference between most professions and programming is that we have open source. The equivalent of that in the real world is getting blueprints to build our tools and then building them ourselves, with no guarantees.

Have you met farmers? They will readily tear apart their equipment to fix an issue or modify the tool to make them more ergonomic. This trend of massive multi-million dollar John Deere combine harvesters with DRM widgets that you need to take to a specialized tech to get fixed, is a relatively modern one, and one detested by virtually every farmer.

This was and is quite common in any blue-collar field where works don't always have the money or time for a brand new jawn every time something goes wrong.

I love estate/yard/garage/barn sales and going through the tools and reading the tales they tell from their wear patterns, field repairs, revisions, and hacks from their owner.

Case in point, here's a tool which has gotten multiple leases on life through modifications. https://youtube.com/watch?v=oTA513ttrbQ

I worked on a farm. I think the things you're bringing up are still subtlely different. Modifying something you can see and touch for some unintended purpose versus modifying some piece of software because it doesn't work are miles apart from each other.

For example, I have a graphics project where screen tearing suddenly started appearing although my code didn't change, and the tearing wasn't there before. Is the issue in Skia, OpenGL, the graphics driver, the Intel or NVidia GPU, or the OS? Or is it some latent issue in my code that started showing up because of a change in these dependencies? Or is it some other complex interaction between multiple dependencies? I couldn't possibly know, and I actually don't think there is anybody that actually knows. And there is zero chance I could ever figure it out. I mean, it at least appears it was a driver issue as after some updates and a reboot it just went away, but there is zero insight into why or what actually made it go away.

If I modify a plow because some part as originally designed was flaky and constantly broke, you usually know to a pretty good degree why your fix works.

In software, the abstractions are such that it is practically impossible atbtimes to understand.

That to me sounds like a difference of degree or heap problem, rather than a categorically different condition.

Sure, welding back on a hardpoint that broke off a plow is concrete and obvious (maybe that's analogous to fixing a broken dependency that got renamed), but there are other fixes that definitely fall under "I don't know why it works but it works". I've had electrical gremlins and fixed them by grounding something that looked like it ought to be grounded, and was grounded when I tested for continuity, but nonetheless was acting floaty. It was probably a loose connection elsewhere in the system, but I didn't know that for sure, nor do I even know that it was the problem in the first place.

Software definitely reaches insane depths of complexity, but again, if you can dig down and understand the abstraction well enough to attempt a fix, and the problem goes away, isn't that good enough? The real difference is mechanical systems typically only have a few layers of abstraction, while software has dozens to thousands of layers of abstraction.

> That you can open them up in VSCode, look around and figure out any bugs or issues you're having and even open pull requests to improve them.

I think the real skill then is to learn to navigate big codebases in few days time than taking a few weeks and then feel dejected by the time spent and still unsure.

I often feel ambitious for such endeavors but navigating big codebases take time, any tips?

It helps if you can get your IDE's indexer configured, so that you find refererences to functions and variables reliably.

More importantly is to use an IDE with a good fast global search function and get comfortable with it. At least for me, 99% of navigating a large codebase is global search.

Is there a way to get vscode to this level.
Not using VSCode currently but I think pretty much yes.

You can goto or peek definition in VSCode. And the find tool has the option to find all occurrences throughout your project.

For C++, I use clangd (works fine for GCC projects). The only config it needs is the path to compile_commands.json, which can be automatically generated by CMake and some other build systems. For TypeScript no config is needed. For Java there is the redhad Java plugin in VS which provides good indexing.
Or use an ide that doesn't need indexed configured and just works
This. My favorite language for this is Clojure. There's usually not much library code to sift through because it's so terse.
"I was an ordinary person who studied hard. There are no miracle people. It happens they get interested in this thing and they learn all this stuff, but they’re just people." - Richard Feynmann
I was working with a decoder for the LD-CELP voice compression algorithm. The machine it was running on was compute-bound and the playback stuttered a lot. I dug into the code, then went to the papers describing the algorithm, then went back into the code and modified a piece of it to be better tuned to the data I was processing. In the average case I probably made the code worse, but in my specific case I sped up the thing by 30x. That gave me confidence in a few things:

1) I can figure out issues in other people's implementations.

2) The best solution may not be the best solution for you, and my hunches on optimization might be correct

3) It's fun to look like a wizard to your peers

A programmer's confidence can come from a few different directions.

The chief one, I think, comes from simply having achieved enough challenging problems to the point where one knows that, even if their solution isn't perfect, they can figure out basically anything. The overwhelming majority of programmers can get to this point eventually.

Confidence also comes from the eventual realization that, with few exceptions, the majority of programmers you think are "better" than you are really aren't objectively better. They may have a fancier title, a bunch of GitHub stars, have given presentations, or maybe they've written a book, but this doesn't mean they are a better programmer than you. It might be hard to understand as a novice, because virtually every novice is going to feel inferior for lack of experience. After having gained sufficient experience, to the point where there's seemingly no problem one can't solve, it may become obvious that these programmers one once viewed as elites actually just broadcast their craft in a way that gives them higher authority; I stopped viewing most programmers as being better or worse than each other because I've witnessed enough problems caused by the clever solutions of senior and staff developers. It's just much easier to blame a junior engineer's solution when it doesn't work out. This isn't to say that more seasoned programmers aren't worth their salt, but there's almost never something they have that you can't have, and every programmer out there writes bugs all the time!

And finally, seeing the tech hype come and go over a long enough period of time can eventually bring one to the point where they look back on methodologies, frameworks, and design patterns they were once introduced to and come up with the original thought of "You know, I think that X was kinda bullshit to begin with." I think it's really unlikely that a programmer can have a 20+ year career (or even a 10+ one for that matter) and not come to some conclusions like that. Though it's easy for a novice to get caught up in the churn thinking they've got to learn all the things and feel inferior for not knowing it all, experience may someday teach the programmer that most of the churn was hype and that their own ideas on how to program effectively probably would have worked just fine.

I would say that I gained my confidence by just trying enough times and gaining experience.

Funnily enough, getting bugged by some random people about a bug in one of my projects. I'd built projects before, mostly for the fun of making them, and occasionally for my own use. The first time someone reached out to me on Discord going "Hey, love your tool, but this feature hangs my machine" was a wake-up call that I had actually succeeded in making something useful.
I can tell you what limited my confidence as a programmer. It was correctness, or knowing how to deal with stuff so I don't have to check every line of code once I implemented something new that uses the old stuff in a novel way. For some people, it was apparent right away, but I had to discover that once you design something that works in all cases, you don't have to check wether it works as a part of newer code flow, or bigger use case. Only then my confidence started to compound.

In the late 90s to early 2010s it was fashionable to think of code as a graph of dependencies, and as long as you honor those dependencies when changing the code you would be ok. This is simplifying but pernicious POV IMO - reinforced by the zeitgeist that code is truth and comments (in code or otherwise) may not be. This is a cul-de-sac.

Unless developers see things as nouns or verbs, they don't acquire the ability to scale and see the bigger picture, because there is nothing to hold these smaller entities and connections between them (O(entities^2)). The concepts are the bags. Wish someone would explicitly teach us these things, because, sure as hell they didn't teach it in college.

May be people who have a liberal arts background are good at seeing these things, because one of the things they explicitly study is perspective, framing etc. Engineering is quite limited because we don't explicitly study about that.

When I fully understood that impressive code that I was reading online was written by a real person, and that as a fellow real person, I should be able to learn to understand and write similar code. I experienced this when I was doing DailyProgrammer challenges from Reddit in highschool. I was doing most of my programming in Java at the time, but much of the Java solutions that I saw were using Java 8 features that looked alien to me. Realizing that it was not alien, and would just take time to learn, was a massive confidence boost for me for some reason.
For me it was reviewing code and seeing a ton of diffs - to help realize the incremental change nature of coding and seeing small pieces of code in relative isolation
There’s no “right way” when you’re a learner.

You’ll gain confidence when you realize you can just hack away until it works and that is OK.

I think working on a lot of small changes across different areas of a project and starting to feel momentum.
I just worked through making a 2D physics engine via Gustavo Pezzi's course on pikuma.com

Seeing shapes collide, bounce, roll, and understand exactly what was happening in the code I wrote was a huge confidence booster.

(comment deleted)
I was a self-taught guy largely working by myself. I went to some big Microsoft education conference and met other people and realized most of them weren’t smarter than me or doing something much more complicated.
I have a similar story except the confidence came when I landed my first on-site job. Once I got in the team, the same realization came to me - I am not any worse than these guys here.
Working on teams with great functional test coverage, so it's difficult to accidentally break something big. The mindset that if someone makes a mistake and introduces a bug, it's not their fault, it's the tooling/process's fault has stuck with me throughout my career.
Learning the syntax of my language really well.
Eh, I've worked in PHP for more than a decade professionally and I still need to double check the argument order for array_walk. The syntax of how we express ideas abstractly is probably a better thing to focus on than any specific details tied to the language.
(comment deleted)
Standard library != syntax
Sort of? It depends on the language but a lot of times syntax is just a fancy sugared way to invoke weird functions - i.e. `if (a === b)` might just be `if (a.equals(b))`. I work building tools to make other people more productive so a lot of the time I'll build out functionality I'm implementing using sugared syntax to make it cleaner. In some languages this isn't possible but most modern languages allow metaprogramming of some form or another.
since confidence is orthogonal to capability: positive encouragement and uplifting comments from my seniors did the most for my confidence.

if the capability is the question, well i don't have any answers. 10 years in and the imposter syndrome is still strong.

(comment deleted)
For personal development, it was merely shipping things. The more I published, the better I felt about myself. The more I published, the more I had learned and had to refer to. Now when I'm taking on tasks I can instantly recall how each of the pieces of the problem can be stitched together from things I did previously (or at least know where to look for foundations to build from)

For the confidence? It was working with others. The first job I had I got to sit down with one of the company's programmers as part of my on boarding and watch him work through tickets. After I saw just how flawed everyone was I felt a lot better about myself. I suppose that's a weird thing to say: Oh he was pretty shit, so I shouldn't feel bad about my poor performance...but that's not the point I want to make really. More that, it is wrong to compare your efforts to learn and grow against the final product of others. Once you sit down with the experienced devs and see how they shape and form the product and all the bumps along the way it doesn't feel so bad to struggle on your own.

Ultimately the skills that I honed that gave me the best boost in confidence were not really the direct programming parts where I put letters and numbers in files. It is the debugging. Understanding how things move and where to look for problems makes me feel like I can solve any problem with the right tools.

My perspective is different than what has been posted, so here it goes.

I knew I could outwork everyone and brute force being a better programmer by a function of continuous improvement through hours of work. (btw I was wrong, there is always someone out there that will outwork you...and they will be smarter than you, but hey it made sense to me).

I didn't realize I made it until

a) People would recognize me in meetings because of my code

b) New programmers started to ask me for advice

...now I'm in the Sr Devs, Tech Leads, and Lead Architects ask me for advice level. Honestly, I still have the same attitude, if I work effectively, I can keep getting better. Notice that I remove the "work harder" part.

For me the moment was a year or so into my first programming job, this was in the 90s. We were debugging some device driver issues and found that we'd made bad assumptions about the order in which things happened between our interrupt handlers, device driver thread and application threads. Once I took the time really mapping out what all can happen in an asynchronous environment and then setting up long-running stress test with multiple clients it clicked in place that you really have to continuously test your mental model against what could happen in the real world.
Shipping and getting to see customers using and enjoying what did not exist before I typed it into being.

A simple CRUD app is magic to the average user, if it does something they haven’t seen done in software before - and that could be as simple as some calcs in a custom domain.

The first customer who tells you “this is great”, if it’s anything like my experience, changes everything.

It also helped that some of my early forays were to clean up code by a guy ten years my senior which was rammed with bugs - fixing those one by one was satisfying and helped me earn trust with my team and in myself.

The brutal truth of it is that while there is beauty in the craft, in the end shipping is what matters. Getting things in front of the people that need them.

There's a nice concept in theater that says, effectively, "if you blow a line, don't broadcast it to the audience." The subtext of that is, simply, the audience doesn't know any better and what they don't know, won't hurt them.

By and large, this applies to many fields, and certainly to software development. The users, in the large, do not care over a huge amount of stuff that we, as practitioners, care about.

We're in a results business. The users care that they get the report they want or can update the data they're supposed to. How that's done, in the back end, is filed under "blah blah blah".

The reason this is important is not as an excuse to do crummy work, or to not be passionate about the minutiae that consumes us, but to be cognizant that as long as the end result is functional and capable for the user, it's OK for the processes that get to that point to be a bit messy. Don't beat yourself up over a lot of the things we beat ourselves up, or criticize others over.

Ship. Get your code in front of users, interact with them to make their experience better. We are in a service industry. Strive to be better, but you only get better by shipping, and moving on.

The confidence is that, for the most part, "it's just software" and you can fix it. You can change it later. You can correct mistakes.

Another saying is that you're not a professional until you've lost production data. Thankfully in our work, loss of production data rarely kills people, in contrast to the path of becoming, say, a doctor. If you are in an industry where people can be hurt, then that's going to be more rigid, for obvious reasons.

I have lost data, I have been SCREAMED at by clients, I have stood under the withering "What has just happened here" glare from management. It happens. Ah, the warm camaraderie of being thrown into a data entry pool to help rekey some of the 6 months of data you destroyed. A cold truth in our business is that it has to happen, as we only learn through practice, and through practice, sometimes we miss.

But to do that, we have to ship. We have to get the work out there, "I don't test often, but when I do, it's in production". Sometimes you just got to open the firewall up, let the traffic in, and see what happens.

A cynical view is the that the more we ship, the more we learn what we can get away with. There's a truth to that as well. We're all victims of not listening to "you're not going to need it". For all sorts of values of "it". But that doesn't mean we can't keep those concepts close, practice them as best as we can (because, truth is, sometimes you do need it!).

Through the work, and shipping, you learn the balance, and get more comfortable with it.