Before you try to do something, make sure you can do nothing
devblogs.microsoft.com
devblogs.microsoft.com
"I don't know how to do X, Y and Z."
Before I begin working on the real solutions for X, Y & Z I start by making a test program that does X. It's ok if it requires a ton of scaffolding, or canned data... the important thing is to do X. Then I do the same for Y & Z.
Now that I understand the problem I do a software design. I do this on paper and I purposely do it super quick and not caring about how neat it is... and I throw away designs rapidly as I iterate and improve. Eventually the paper crumbling slows down and I approach a real design.
This is finally the point that I can write "real" code. The unknowns are gone and all the software design iteration happened on paper.
I totally agree, and I work in a similar way. I find that the time I gain down the road drastically makes up for the time spent on the prototypes.
This doesn't apply to every problem, mind you - I only advise this type of working either if you really want to understand something, or if you have a good hunch it is going to be used for a while (or may be). One off scripts, there's not much gain - just get them working. Code that is part of a system, absolutely. It should be done this way. Throw it away - and maybe throw it away again.
... or you think it's good enough, put it in prod, claim the impact, and move to the next problem.
Can you check in even a tiny change to the source-code repository? A new project's first commit often triggers a slew of politics: wait, you're building what? Where's your design? Who approved this? Why are you naming it this and not that? We already built this; why are you building a second one?
What's your installer story? For installable software, do you know how to produce the installer for it? Too often the installer work is left until just before launch, at which point it's way too late. For a service, do you know how to deploy anywhere but localhost so your team can start doing internal demos?
Can anyone besides you build your hello world code? The release team often has their way of doing things, and it's not what Visual Studio spits out from its new-project template. Might as well integrate early, rather than wasting time solving problems for a build system you were never going to use.
FWIW, I personally start new projects with a Software Design Intent document that explains what we're attempting to do and why with full disclaimers about what we do and don't currently know. Maybe I've just managed to hedge these people out.
That's great. But having written it doesn't mean that everyone has read it. And not everyone who has read it will agree with it. Or maybe the person who you thought would champion it has left the company. Or maybe the world has changed since you wrote it. You've probably heard the expression "no business plan survives first contact with customers." A corollary, in anything but an extremely top-down organization, is that version 1 of your planning doc is unlikely to perfectly describe the final product.
> a) the question asker knows better, and b) that no one bothered to think of any of this to begin with
Eh, that's assuming a fair amount of antagonism on both sides. Most people are reasonable and rational, and after the initial "WTF? Are you crazy?" handshaking, everyone can get on the same page. But that process takes time. If your first contact with a downstream person/team/group is right before your 1.0 is supposed to ship, then you're more likely to treat them as a roadblock. If you instead involve them early, get the WTF moments out of the way, and take the time to develop a relationship, then you've turned a potential roadblock into a stakeholder and advocate, and your SDI becomes a true representation of consensus, rather than a bludgeon.
It's just describing the intention of the team at the onset. Nothing more. I will not update this document once things get really rolling, that's why we have design documents elsewhere. What I personally do with this thing is I put a checklist of "approvers" at the bottom and I send an e-mail to them all asking them to review it and give their nod of approval or otherwise.
FWIW I'm in agreement with everything else you're saying here, though.
Sounds like a good way to nip the creative aspect in the bud.
Sure, if a client comes to a software vendor with a clear plan, draw up good specs, requirements etc. before doing any design (or coding). Or code a quick 'n dirty proof of concept solely to hash out what exactly those requirements are. Same with existing codebase if you want to add feature X and scope of that is well defined.
But I suspect quite a few applications / projects / websites / tech startups / software libraries have started life with a "hey, that's neat. Let's take X, glue onto Y, tinker & play with it, see what we can do with that".
Small / cheap 1st iterations don't involve much wasted effort anyway. Worst case = 0 useful code but you have learned something.
When utility has shown & you get a sense of where you want it to go, then bring in the design aids for a proper v1.x, 2.x etc.
The odds you're the first one to have thought of something are [practically] zero
The odds you're the first to implement something are only slightly higher
When you find out someone else has already built 89% of what you're looking for (and 213% of what you hadn't thought of yet, because they already ran into the corner cases and what-ifs you couldn't possibly have thought of until you hit them), you can save yourself (and the organization) loads of effort by extending what's there instead of building from scratch
Maybe you're the first to think of it and first to build it
But you're probably not - and getting that early feedback from someone who sees what you're working, and says, "why are you building what Sally & Tom built last spring?" is absolutely invaluable
Putting on my sysadmin hat: Please do this. Please.
Think about the people who have to install and maintain your software.
Talk with real sysadmins about how they deploy software if you don't have experience doing it. Think about how you'd deploy, remove, and update it on large numbers of machines-- particularly where the users don't have administrative rights or where the machines are automatically-provisioned "cattle".
Also, use off-the-shelf installer systems, if you can, versus rolling your own. The maintainability will be a lot better.
Not saying it's better, but it can be the difference between achieving something and nothing.
You'd have to be really new to write a bunch of code without ever testing it working incrementally, likely because you've never encountered it before. A couple of painful debugging sessions will quickly make you realize you need to test your assumptions one by one in the right order if you are to ever have a chance of making an unknown program (i.e. a program you are writing for the first time) work. All the stars have to align, because one wrong error will throw everything right off. Debugging is the act of visiting each possible source of error and validating it works. The best way to do this is to build the program incrementally (which again, I expect programmers to quickly realize).
I've been programming professionally for 10 years. And even in my early years, I could code all day without running and normally it works as I expected or if an error occurs it's quick to fix.
Sorry if that sounds like bragging, but I'm sure there are others out there that are like this- just saying it's not an absolute truth, but I believe it to be generally true.
> It had passed through our new modeling tools, through two different intermediate converter programs, had been loaded up as a complete database, and been rendered through a fairly complex scene hierarchy, fully textured and lit
They didn't do the bare minimum modeling without tooling, didn't hack together (or avoid making) barely enough converter programs, they didn't hack together an in-memory database as a prototype, and they didn't do the bare minimum rendering to show a triangle.
They built out a fully working system, layer by layer, the entire breadth of each layer, until they got to the rendering and display layers. At the end of it, they got a triangle. But, since they put in all of the effort to build a strong foundation, the second triangle is 1000x easier to make than the first.
The operators wanted a certain box to "turn green", and they told a coworker about it.
So he hardcoded it to green, not using the status from the PLC.
The thing is, he was already working on the request, but getting the communication up and running for the PLC is quite involved, so to stop people from bugging him he just "turned it green"
*PLC means programmable logic controller, aka simple and predictable industrial computer with lots of I/O
package foo;
public class Hello {
public static void main(String[] args){
System.out.println("hi world"); } }
As for the build and run,
javac foo/Hello.java
java foo.Hello
That's it. This is covered in chapter one of any Java book.
Am I misunderstanding your point or something? It sounds like you are complaining that Java is hard to get a simple hello world running without IDE help, but I must surely be misunderstanding you, because that would only be the case if you refused to do even the most cursory reading beforehand
I would invite you to re-read your code and see how many different things you need, and how they are expressed differently in different places.
"Any Java dev" - I'm not a Java dev. I'm a dev who sometimes writes Java. I am not good at remembering equivalent commands across languages. For instance, there's not much call to remember System.out.println when writing Spring Boot apps.
Different people have different skills. For instance, some people can communicate without insulting people. This is a valuable skill to grow a career.
Also, there are things not shown, like environment variables.
I love your comment as an example of "This is so simple, there are only 20 things that you have to get perfect, any idiot can do that"
This is an example of making things harder on yourself for no reason and then complaining about your tools instead of your own artificial restrictions. There's no shame in looking some syntax up if you haven't done it in many years.
You said there were 20 things in there but I only count two unique values that aren't just boilerplate you could look up in a minute-- the package name and class name.
I just get tired of these kinds of superficial complaints about java "lol hello world takes 4 lines instead of 1" -- that barely affects any program that's more than 20 lines long. I just think you're part of the generation that grew up making fun of Java and it's just reflexive at this point. But, it's unfair.
Compare to a language like ruby -- hello world is one line but I have to learn a bunch of weird symbols
Lol, that said, the latest Java edition now has a way to avoid the public class business so your hello world can just be 3 lines, one of which is the closing brace. They did it to shut up these kinds of trivial complaints.
public class Hello {
public static void main(String[] args){
System.out.println("hi world"); } } `
#As for the build and run,
javac foo/Hello.java
java foo.Hello
---
Structure
You need: a package*, a class*, and a main method*
3
Code
The package needs to match the folder* : 1
The class needs to be public* and match* the file name; Main method needs public* and static* and take an array of String args*; you need to remember the call to output to console* : 6
Build and Run
Have to remember javac* and java* : 2
javac and java have to be available when you call them
0 - 10 [10 if you have corporate IT managed machine] (I'll call this 1)
Remember where to put the package for each call : 2
---
So I only got 15. I could have probably got more if I tried. Just because each of those 15 things is trivial by itself, does not mean you can ignore them.
I got to do 'the algorithm game' with middle-schoolers. I was the "Peanut Butter and Jelly Sandwich Robot". I took their instructions literally. It was a hoot. It's less fun when the computer isn't trying to help you.
That may not sound like much, but it's the kind of thing that is incredibly hard to figure out if you don't know what the problem is. Command lines are kind of unforgiving. When something is missing, the errors are often unhelpful. And if your next crack at it doesn't work, you get the identical error.
Googling stackoverflow isn't going to help, either.
This isn't supposed to be a major challenge. But it can really throw off a beginner. And even an experienced developer will feel stupid to have to say, "Sure, I can program in 30 languages, but I don't know how to install the FooLang compiler."
* Watch the test fail first. Write a test FooMethod_Should_Do_X. Confirm it fails. If it passes, it means you're testing the wrong thing. Once it fails, make it pass. Once it passes, make it pretty while still passing
* The simplest thing you can do is nothing. As a developer, your first job is to talk customers down from that feature request without annoying them too badly. "What bad things happen if the app doesn't do X?", "Explain the business value of X to me. About how much time or money will this save?".
* This applies not just in code. In personal interactions, saying nothing or doing nothing is often better than prematurely doing the wrong thing. There is a time to say and do something, but it is usually later than we think and not on social media.
Depending on the organization, by the time it got to you, there may have already been discussions about scope and business need.
Unless you're an SME, your evaluation of what is needed is not better than that of those who are requesting the work. Sure, your knowledge of the effort is better, but that's not the same thing.
There's a decent chance you're going to come across as difficult to work with and then you're going to build a solution that doesn't fully meet their needs.
I get where you're coming from, but I've spent time on the other side. Requirements take negotiation and collaboration.
It gives you some sense of the affordances you need and the current value of having done nothing so far.
It also helps with prioritization - is it a must, a should, a could, a wish?
As a dev I like to say my job is to translate English into nerd. Non- or semi- technical people should communicate the business problem and then together we’ll work towards the best solution. Sometimes the solution is code.
I support a lot of scientists-who-sometimes-code, and I get this all the time. They bring me in and show me the 5000 line Python (or Matlab if I'm unlucky) script they've written. "It doesn't work."
Turns out they've written 5000 lines of code without having ever once tried to run a smaller piece while they were writing it. Not even once. They write the whole thing and THEN when it doesn't run on the first try, they don't know what to do.
- Write the scaffolding test to call the new "thing"
- Add basic assert to check your thing was called with right input
- Add more logic to "thing"
- Incrementally add more asserts to check for output, side-effects etc.
In my steps above, i was switching the order in between 3 and 4. Adding logic to "thing" first and then updating the test to match; mocking and patching my way across. So implementation first and tests later. Lazy TDD as another commenter mentioned.
It's well worth reading, very short (and pretty funny also).
But yeah, the approach he suggests is to write the minimal failing test, then make it pass, then repeat. Red, green, refactor is the short form of his approach.
But your misconception appears to be incredibly common, I've heard it from so many people over the years. No idea where it came from, but it's nonsense (IMO, obviously).
The only time I write more actual tests is when squashing bugs.
I write tools that help me test things. There are a lot of people like me who don’t work on big systems or write production code. Our priorities are different.
I need to duck tape a thing to another thing quickly. I happily accrue tech debt because getting my testing done today (not regression testing, but primary testing) is paramount. Later I will declare bankruptcy (abandon the code).
I work alone, mostly.
The dynamics change when I work with other guys, or when my software has to serve as a persistent framework into the future.
I’m just saying there are a lot of programmers like me. We are also interested in good craftsmanship— but we operate in a “battlefield” context, not a drawing room context.
and later he adds: "Too often, I see relatively inexperienced developers dive in and start writing a big complex thing"
He is advicing to before start building something, make sure that the context is rightly settled.
https://www.youtube.com/@JustForFunc and https://www.youtube.com/@fasterthanlime are good exceptions to this. I want to see the process of building out something complicated one bit at a time, including all the mistakes and dead ends.
The first hardware-accelerated 3D API I used was the first version of Direct 3D in 1996. It took days to get get single triangle.
Carmack described it well:
https://www.bluesnews.com/archives/carmack122396.html
Direct-3D IM is a horribly broken API. It inflicts great pain and suffering on the programmers using it, without returning any significant advantages. I don't think there is ANY market segment that D3D is apropriate for, OpenGL seems to work just fine for everything from quake to softimage. There is no good technical reason for the existance of D3D.
...
Many things that are a single line of GL code require half a page of D3D code to allocate a structure, set a size, fill something in, call a COM routine, then extract the result.
I was pretty defeated for a while, took me months to get motivated to try it again using OpenGL 1.1, and it just worked. I don't know how faulty my recollection is but I think I remember it was also much easier to search the web for tutorials/discussions about OpenGL than D3D at the time.
It's all justifiable of course, and Vulkan actually isn't too bad if you just want to push your own vertices within an engine where someone else has already written all that scaffolding.
This is a very good thing to keep in mind when estimating.
Although back in the day I was very productive with a Lisp REPL environment, although I still used the 'write-a-little-bit-and-test' approach.
Excellent advice!
Just last week I did JSON transformations in a succession of little Node JS programs that are chained by pipe. Each program does one step of the transformation; it starts its life by outputting what it receives, then evolves to actually manipulate data.
It's an approach that's quite safe and rarely breaks, and when it breaks it's easy to find where the problem is.
I frame up small projects that replicate the key parts of the software I need to interact with.
So often you find yourself trying to integrate with outdated code vases full of unknown dependencies and customisations that look nothing like the docs or sample code.
Even the smartest people can feel overwhelmed!
Gives your confidence when you can get a feature working from a fresh project!
But I find that I take a similar approach to Chen. Maybe not quite so rigorous.
- Start with a console app (actually I always keep a console app in the solution for isolating bits of code I want to debug)
- Scaffold the part where said code needs to go
- Once the code from step 1 is ready, stick it in the scaffold and debug until it looks good
- Work on further tests (load test, code coverage, variety of inputs)
One thing that has stuck with me from Steve McConnell’s “Code Complete” is his use of PDL (“Program Description Language” - I think).
The general idea is to write everything out in pseudo code first (at the function or small group of functions level - _not_ the whole program or component).
My PDL is basically a slightly more hand wavy dialect of Python.
I omitted this step in my prior post, which only described the coding aspect.
But don't get me wrong: I'm not a great programmer. =)
So I’m kind of making it fast first, or at least laying the foundation for it. Async has to be in place from the start due to function colouring. I spent a while being entirely “unproductive”, setting it up.
this is great strategy for debugging & so code reviewers have an easier time reviewing your incremental changes.
On newer teammates, I usually reject code reviews if they don't follow these practices (if the company's engineerimg culture is compatible with it)
But can you turn it off?
To concentrate one on thing is to ignore 1000 things. If that can't be turned off, becomes habitual, permanent, fades to invisibility as constants and habits generally do, one might be suffering from a massive distortion of perspective and not even know it.
Then use a good one! :)
This is tongue in cheek, of course, but think about steps 2 and 3; this is where you want to verify that you understand when the method is called and what it gets as input. The method might receive six nested structs as arguments, how many tries does it take you to print all the relevant information? Does what you're trying to print have a useful string representation?
Set a breakpoint in your Invoke method. Inspect the arguments as deeply as you need to. Check the stack to see where the method is actually being called from. Verify that you can access any other objects you need and that they are in a sensible state. One good breakpoint can save you dozens of edit-compile-run cycles.
printf debugging shines when you need to investigate a property over many individual calls - What array sizes is this function being called with? At what point does this optimized calculation diverge from the reference implementation? Are functions A and C ever called in succession without B being called in between?