But for the rest of us (especially myself), it seems to be more like an interplay between thinking of what to write, writing it, testing it, thinking some more, changing some minor or major parts of what we wrote, and so on, until it feels good enough.
In the end, it's a bit of an art, coming up with the final working version.
I can relate to that.
But the point of the parent of your post is correct, in my opinion. The Git interface sucks. Subversion's was much more consistent, and therefore better. Imagine how much better Git could be if it had had a little more thought and consistency put into the interface.
I thought it was pretty universally agreed that the Git interface sucks. I'm surprised to see someone arguing otherwise.
Depends what you consider “universally agreed”.
At least one person (me) thinks that: git interface is good enough as is (function>form here), regexps are not too terse - that’s the whole point of them.
Related if you squint a lot: https://prog21.dadgum.com/170.html
I actually migrated my company from CVS to SVN in part so we could do branchy development effectively, and also so I personally could use git-svn to interact with the repo. We ended up eventually moving Mercurial since Git didn't have a good Windows story at the time. Mercurial and Git are pretty much equivalent in my experience, just they decided to give things confusing names. (git fetch / pull and hg fetch / pull have their meanings swapped)
It's very rare that a new piece of software just completely supplants existing solutions as widely and quickly as git did in the version control space.
Their greed literally killed their own business model, and brought us a better versioning system. Bless their greedy heart.
This is really important for maintenance later on. If it's too complicated now to keep in your head, how will you ever have a chance to maintain it 3 years down the line? Or explain it to somebody else?
I do data flow diagrams a lot (to understand the domain, figure out dependencies, and draw rough component and procedure boundaries) but leave the details of data formats and APIs to exploratory coding. It still makes me change the diagrams, because I've missed something.
In the end, I find my mental picture is still the most important. And when that fades after a while, or for code written by someone else, then I just have to go read the code. Though it may exist, so far I haven't found a way that's obviously better.
Some thing I've tried (besides commenting code) are doing diagrams (they lose sync over time) and using AI assistants to explain code (not very useful yet). I didn't feel they made the difference, but we have to keep learning in this job.
Code also functions as documentation for the actual process. In many cases “whatever the software do” is the process itself.
I map out on paper the logical steps my code needs to follow a bit like a flow chart tracking the change in states.
When I write code I'll create like a skeleton with placeholder functions I think I'll need as stubs and fill them out as I go, I'm not wedded to the design sometimes I'll remove/ replace etc whole sections as I get further in but it helps me think about it if I have the whole skeleton "on the page"
How very biblical. “And Torwalds saw everything that he had made, and behold, it was very good. And there was evening, and there was morning—the sixth day.”
> The seventh day he rested
This is obviously Sunday :-)
I presume you're using zero-based numbering for this?
This is special because most real world systems has a lot more dependencies. That’s when experimentation is required. Because one cannot know all relevant API’s beforehand and their behaviors. Therefore the only way is to do it and find out.
Algorithms are in essence mathematical problems, therefore is abstract and should be able to be solved in the head or use pen and paper.
Reality is that most programming problems are not algorithms but connecting and translating between systems. And these systems are like blackboxes that require exploration.
This type of developer tends to prefer building software like this. There is a whole crowd of hardcore C/C++/Rust devs who eschew taking dependencies in favour of writing everything themselves (and they mostly nerd-snipe themselves with the excessive NIH syndrome, like Jonathon Blow off writing Powerpoint[1]...)
Torvalds seems to be mostly a special case in that he found a sufficiently low-level niche where extreme NIH is not a handicap.
[1]: https://www.youtube.com/watch?v=t2nkimbPphY&list=PLmV5I2fxai...
Therefore if you walk through the woodland thinking about a program to write in C there is exactly zero interrupt to check docs to see how some dependency might behave. There is no uncertainty over what it can do, or what things will cost you writing another language to do reasonably.
Further, when you come to write it, there's friction when you think "oh, I want a trie here", but that's of a very different nature to "my python dependency segfaults sometimes".
It's probably not a path to maximum output. From the programming is basically a computer game perspective it has a lot going for it.
Lua is basically the same. An underappreciated feature of a language is never, ever having to look up the docs to know how to express your idea.
and these are the type of coders that also favor types.
whereas if you do the 'informational' type programs as described by Rich hickey -- ie interact with outside systems a lot. you will find a lot of dependencies, and types get in the way
That sounds a bit weird. As I remember, linux developers were using semi-closed system called BitLocker (or something like that) for many years. For some reason the open source systems at that time weren't sufficient. The problems with BitLocker were constantly discussed, so it might be that Linus was thinking about the problems for years before he wrote git.
My point is, he thought about it for some time before he was free to start the work, then he laid down the basics in less than a week, so he was able to start using Git to build Git, polished it for a while and then turned it over.
Here's an interview with the man himself telling the story 10 years later, a very interesting read:
https://www.linuxfoundation.org/blog/blog/10-years-of-git-an...
I get a general idea, then start writing code; usually the "sticky" parts, where I anticipate the highest likelihood of trouble.
I've learned that I can't anticipate all the problems, and I really need to encounter them in practice.
This method often means that I need to throw out a lot of work.
I seldom write stuff down[0], until I know that I'm on the right track, which reduces what I call "Concrete Galoshes."[1]
[0] https://littlegreenviper.com/miscellany/evolutionary-design-...
[1] https://littlegreenviper.com/miscellany/concrete-galoshes/
Now I could have spent that time "whiteboarding" and it's possible I would have come close to the same solution. But whiteboarding in my mind is still guessing, anticipating - coding is of course real.
I think that as you gain experience as a programmer you are able to intuit the right way to begin to code a problem, the iterating is still there but more incremental.
An experienced programmer is also more cognizant of the importance of architectural decisions, hitting the balance between keeping things simple vs abstractions and the balance between making things flexible vs YAGNI.
Once those important bits are taken care of, rest of it is more or less personal style.
I never hold all the code designed in my head at once, but it's more like multiple linked thoughts. One idea for the overall structure composed of multiple smaller pieces, then the smaller pieces each have their own design that I can individually hold in my head. Often recursively down, depending on how big the given project is and how much it naturally breaks down. There's certainly unknowns or bugs as I go, but it's usually more like handling an edge case than anything wrong with the design that ends in a rewrite.
I wanted to work that way, when I was younger, but the results were seldom good.
Good judgment comes from experience. Experience comes from bad judgment.
-Attributed to Nasrudin
Apparently this quote has been attributed to an Uncle Zeke :) [0]
One of my "go-tos" from him, is the Smoke Seller[1]. I think that story applies to the Tech Scene.
I first heard the GC quote as attributed to Will Rogers, then, to Rita Mae Brown.
[0] https://en.wikipedia.org/wiki/Nasreddin
[1] https://www.tell-a-tale.com/nasreddin-hodja-story-smoke-sell...
My only optimization of this process is to use Java and not just throw out everything, but keep refactoring. Idea allows for very quick and safe refactoring cycles, so I can iterate on overall architecture or any selected components.
I really envy on people who can get it right first time. I just can't, despite having 20 years of programming under my seat. And when time is tight and I need to accept obviously bad design, that what makes me burning out.
Good design evolves from knowing the problem space.
Until you've explored it you don't know it.
I've seen some really good systems that have been built in one shot. They were all ground up rewrites of other very well known but fatally flawed systems.
And even then, within them, much of the architecture had to be reworked or also had some other trade off that had to be made.
As I've grown older, the importance of architecture over micro decision has become blindingly apparent.
The micro can be optimised. Macro level decisions are often permanent.
The more crap you add the harder it is to fix bad architecture.
And the crap is often stuff that would be trivial to add if the bad architecture weren't there, so if you fix that you can add the feature when you need it in a week.
What I try to do is think about the classes of functionality that might be needed in the future. How could I build X feature in a years time?
Leave doors open, not closed.
I would point out, though, that that part also touched understanding requirements, which is many times a very difficult process. We might have a technical requirement conjured, by someone less knowledgeable about the inner workings, from a customer requirement and the resolution of the technical requirement may not even closely address the end-users' use-case. So, a lot of time also goes into understanding what it is that the end-users actually need.
Indeed, and this limitation specifically is why I dislike VIPER: the design pattern itself was taking up too many of my attention slots, leaving less available for the actual code.
(I think I completely failed to convince anyone that this was important).
The most I've done with emacs (and vim) is following a google search result for "how do I exit …"
Then I break that down into small and smaller steps.
Then I hack it together to make it work.
Then I refactor to make it not a mess.
i am not smart enough this week to debug a raytracer on paper before typing it in, if i ever was
things like hypothesis can make a computer very powerful for checking out ideas
But this also makes it difficult to give accurate estimates because you sometimes need to prototype 2,3 or even more designs to workout the best option.
> writing code should be actually seen as part of the "thinking process".
Unfortunately most of the times leadership dont' see things this way. For them the tough work of thinking ends with architecture or another layer down. Then the engineers are responsible only for translating those designs into software just by typing away with a keyboard.
This leads to mismatch in delivery expectations between leadership and developers.
The prototype should provide you with cast iron certainty that the final design can be implemented, to avoid wasting a huge amount of effort.
I kind of imagine what the end result should be in my head (given value A and B, these rows should be X and Y), then write the tests in what I _think_ would be a good api for my system and go from there.
The end result is that my code is testable by default and I get to go through multiple cycles on Red -> Green -> Refactor until I end up being happy with.
Does anyone else work like this?
Like, I expect it should look one way but after I'm done with a few TDD cycles I'm at a state that's either hard to get there or unnecessary.
I think this is why some people don't like TDD much, sometimes you have to let go of your ideas, or if you're stuck to them, you need to go back much earlier and try again.
I kind of like this though, makes it kind of like you're following a choose your own adventure book.
Right tool for the job, as always! The only thing I can't stand is blind zealotry to one way or the other. I've had to work with some real TDD zealots in the past, and long story short, I won't work with people like that again.
And what most people can't do, such as keeping in their heads absolutely all the concepts of a theoretical computer software application, is an indication that real programmers exist on a higher elevation where information technology is literally second nature to them. To put it bluntly and succinctly.
For computer software development to be part of thinking, a more intimate fusion between man and machine needs to happen. Instead of the position that a programmer is a separate and autonomous entity from his fungible software.
The best programmers simulate machines in their heads, basically.
Yes, but they still suck at it.
That's why people create procedures like prototyping, test driven design, type driven design, paper-prototypes, API mocking, and etc.
EDIT: Unless you're saying that SOLID, VIPER, TDD, etc. are already alien invaders from a perfect world and only good and skilled humans can adhere to the rules with uncanny accuracy?
Personally, I think good developers get characters on to the screen and update as needed.
One problem with so much upfront work is how missing even a tiny thing can blow it all up, and it is really easy to miss things.
Plenty of good developers use the code as a notepad / scratch space to shape ideas, and that can also get the job done.
> What is really happening?
> • Programmers were typing on and off all day. Those 30 minutes are to recreate the net result of all the work they wrote, un-wrote, edited, and reworked through the day. It is not all the effort they put in, it is only the residue of the effort.
So at least there the article agrees with you.
Whether you plan before pen meets paper or plan while noodling is a matter of taste.
How I work:
- First I make a very general design of modules, taking into account how they are going to communicate with each other, all based in experience with previous systems.
- I foresee problematic parts, usually integration points, and write simple programs to validate assumptions and test performance.
- I write a skeleton for the main program with a dumbed-down GUI.
From that point on, I develop each module and now, yes, there's a lot of thinking in advance.
Then I learned TDD, and now I can discover the design while I code. It's a huge improvement for me!
I think it's just two ways of doing the same thing.
first is the urge to write the whole prototype yourself from scratch. Not necessary, better avoided. You should just hack some things together, or pick something close to what you want off github. Idea is to have something working. I am a big proponent of implementing my ideas in a spreadsheet, then off to some code.
Second is modern software solutions are complex (think kubernetes, cloud provider quirks, authentication, sql/nosql, external apis) and easy to get lost in the minutiae, they shroud the original idea and takes strenuous effort to think clearly through the layers. To counter this, I keep a single project in my language of choice with the core business logic. No dependencies. everything else is stubbed or mocked. It runs in the IDE, on my laptop, offline with tests. This extra effort has paid off well to focus on core priorities and identify when bullshit tries to creep in. You could also use diagrams or whatever but working executable code is awesome to have.
third is to document my findings. Often I tend to tinker with the prototype way beyond the point of any meaningful threshold. its 2am before i know it and i kinda lose the lessons when i start the next day. Keeping a log in parallel with building the prototype helps me stay focussed, be clear in what my goals are and avoid repeating the same mistakes.
fourth is the rather controversial topic of estimation. When i have a running prototype, I tend to get excited and get too optimistic with my estimates. Rookie mistake. Always pad your estimates one order of magnitude higher. You still need to go through a lot of bs to get it into production. Remember that you will be working with a team, mostly idiots. Linus works alone.
I hand wrote HTML forms and that was not a great plan. I made a dialog generator class in about a half hour that replaced dozens of CreatePermission.html type garbage I wrote a decade ago.
I would say the topic is two-sided.
The first is when we do greenfield development (maybe, of some new part of an already existent software): the domain is not really well known and the direction of the future development is even less so. So, there is not much to think about: document what is known, make a rough layout of the system, and go coding. Too much investing in the design at the early stages may result in something (a) overcomplicated, (b) missing very important parts of the domain and thus irrelevant to the problem, (c) having nothing to do with how the software will evolve.
The second (and it is that side I think the post is about) is when we change some already working part. This time it pays hugely to ponder on how to best accommodate the change (and other information the request to make this change brings to our understanding of the domain) into the software before jumping to code. This way I've managed to reduce what was initially thought to take days (if not weeks) of coding to writing just a couple of lines or even renaming an input field in our UI. No amount of exploratory coding of the initial solution would result in such tremendous savings in development time and software complexity.
* Scribbling out some basic design notes. * Figuring out the bits I'm not completely sure about * Maybe coding up some 'prototype' code to prove out the bits I'm less sure about * Repeat until I think I know what I'm doing/validated my assumptions * Put together a 'more formal' design to share with the team. Sometimes my coworkers think of things that I hadn't, so it's not quite a formality. :) * Code the thing up.
By the time I get to the "code it up" stage, I've probably put in most of the work. I've written things down as design, but nothing hyperdetailed. Just what I need to remind myself (and my team) of the decisions I've made, what the approach is, the rejected ideas, and what needs doing. Some projects need a fair bit on paper, some don't.
Documentation is a good starter/unblocker but once I've got the basics down then I'll run wild in a REPL or something figuring out exactly how I want to do something.
Pure thinking planning is a good way to end up with all sorts of things that weren't factored in, imo. We should always encourage play during the planning process.
I agree. This is how I work as well. I start coding as quickly as possible, and I fully plan on throwing away my first draft and starting again. I do a fair bit of thinking beforehand, of course, but I also want to get the hidden "gotchas" brought to light as quickly as possible, and often the fastest way is to just start writing the code.
But when I do get down to writing code, it very much takes an evolutionary path. Typically the first thing I start writing is bloated, inefficient, or otherwise suboptimal in a variety of ways. Mostly it's to get the relationships between concepts established and start modeling the process.
Once I have something that starts looking like it'll work, then I sort of take the sculptor's approach and start taking away everything that isn't the program I want.
So yeah, a fair amount of planning, but the first draft of anything I write, really, code or otherwise, is something for me, myself, to respond to. Then I keep working it over until it wouldn't annoy me if I was someone else picking it up cold.
"Really good developers do 90% or more of the work before they ever touch the keyboard;"
While that may be true sometimes, I think that ignores the fact that most people can't keep a whole lot of constraints and concepts in their head at the same time. So the amount of pure thinking you can do without writing anything at all is extremely limited.
My solution to this problem is to actually hit the keyboard almost immediately once I have one or more possible ways to go about a problem, without first fully developing them into a well specified design. And then, I try as many of those as I think necessary, by actually writing the code. With experience, I've found that many times, what I initially thought would be the best solution turned out to be much worse than what was initially a less promising one. Nothing makes problems more apparent than concrete, running code.
In other words, I think that rather than just thinking, you need to put your ideas to the test by actually materializing them into code. And only then you can truly appreciate all consequences your ideas have on the final code.
This is not an original idea, of course, I think it's just another way of describing the idea of software prototyping, or the idea that you should "throw away" your first iteration.
In yet different words: writing code should be actually seen as part of the "thinking process".