
Programming by Coincidence - brudgers
https://pragprog.com/the-pragmatic-programmer/extracts/coincidence
======
chaosphere2112
This is something I'm constantly struggling with as I try and teach myself
OpenGL. Tons of tutorials available, but many have little context or
explanation about what it is that they're demonstrating, and I keep finding
them via Google searches for how to use a specific function. I'm almost never
trying to do what they're doing, just having some small degree of overlap.
I've got a copy of the Red Book, and it's pretty handy, but man is that a big
pile of "well, this is pretty close to what I want".

~~~
brandonbloom
I'm having a similar experience now working with FFmpeg. None of the examples
match each other or any of the tutorials on the web. In my experience, this
problem is particularly bad with imperative APIs, doubly so in C. There's just
too many unanswered questions in the API signature, especially around cross-
cutting concerns like global state, memory ownership, and thread safety. Worse
yet, when you do find an example that matches what you want, you quickly
discover an obvious bug and suddenly lose faith in it!

~~~
Terr_
Speculating: This frustration occurs when an API doesn't cue the user to a
useful mental-model. Then the user has to fall-back on a strategy that has a
tinge of ritual lever-pulling.

------
proksoup
Slow down.

Read every line of code.

Learn what it's for.

Learn why you do that pattern instead of the other one.

This process can be made less time consuming if you accept that you will still
need to do things wrongly to keep your job, but over time you gather evidence
and arguments to begin to improve the technique of yourself and your peers.

We all have a long ways to go.

~~~
wjagodfrey
A good practice is to seek out incomprehensible pieces of code and figure them
out so you can perfectly describe how they work.

I don't do it as much as I should, but it's the best way of learning about a
language.

~~~
chaosphere2112
This is my usual technique for learning a new system, though it's usually bug
or feature driven; lots of tracing through every single line of the 1000 line
function

------
spion
The first example is typical with badly designed stateful APIs. You have no
idea what state their state machine is in and it really is pretty hard to
track that in your head - so you try your best to get it to the desired state
before proceeding.

I think we're too quick to shift blame to the API user. Perhaps the designer
is to blame?

~~~
georgemcbay
The API designer is sometimes to blame, in my experience.

A good example of this is in modern programming is the Android API which is
often surprisingly horrible and basically requires reading the API
implementation source code (and maybe even the source code of multiple
different versions since they will behave differently) to fully understand
things that should be relatively simple concepts, like how to properly retain
state across a device rotation when using fragments without getting yourself
into an exception-raising "Illegal State".

Note: I'm not asking for a reference on how to do this specific thing, I
actually know it really well, it is just that the amount I had to learn about
the internals of how everything worked to get to that point is completely
unreasonable relative to how simple it would be if the API were better
designed.

------
philbo
When I first read the book, I remember this maxim resonated with me greatly.
But these days I often feel that all I ever do is program by coincidence.

Granted, the coincidences come in much smaller increments now, and they're
punctuated with unit tests. But they still feel like coincidences.

The deliberate stuff all happens later, when I'm refactoring and the unit
tests are already in place.

~~~
nulltype
I think this may be just how most programming (and perhaps everything else
besides science experiments) has to be done.

If you're writing assembly code, well, computers have some pretty solid low
level abstractions, but do you really understand how many clock cycles that
instruction there is going to take? What about all the error cases?

If you're writing for the web, it's clear you can't even try to really
understand how everything works in all cases across all machines.

Few programmers really understand how computer memory works, but if everyone
were to read this guide
([http://www.akkadia.org/drepper/cpumemory.pdf](http://www.akkadia.org/drepper/cpumemory.pdf))
and really learn it throughly, the net benefit probably wouldn't be that great
vs other things they could do with that time.

It's great fun to track down every last misunderstanding or bug, but it's
probably not worth it over the long term. The benefit of learning the internal
details of a reliable subsystem is often not worth it and it's easier to just
use it and get on with the project.

That being said, when I do go to fix a bug, I try to get to the bottom of why
it happens and not just stop at the first change that works. This is useful
for a number of reasons:

1) If you made the bug, you probably have some misunderstanding of the system
that will be fixed when you understand the bug.

2) If you're spending your time fixing an actual bug, then the
misunderstanding empirically matters because this mistake has been made and
actually affects someone.

3) You tend not to accumulate a lot of superstition in your code, which makes
the code much easier to read.

------
vorg
What's described here as "programming by coincidence" was the meaning of the
negatively-connotated phrase "hacking up a program" in the 1980's, in
Australia/New Zealand at least, before the word "to hack" got redefined as
something positive for the Hacker News crowd. Or maybe the U.S. usage was
always out of sync with the Down Under usage.

~~~
ggchappell
> Or maybe the U.S. usage was always out of sync with the Down Under usage.

I suspect this is the case. I'm in the U.S., and I recall using "hack" in
pretty much the HN sense as early as the mid-1980s.

------
kr0
I'm stumped by the example questions at the bottom. Anyone care to provide
insight?

My best guesses would be

1: Not always in a routine where user input can be obtained

2: Not sure, probably not architecture differences as that seems implied by
the question

3: debug.log won't always be in the current directory

~~~
lhchavez
Some things that could make the functions fail:

1\. In addition to stdin being closed (so gets(buf) would fail and continue
execution without blocking), stdout/stderr are line buffered when writing to a
terminal stream, so they are implicitly flushed when there is a newline[1].
There is no newline, so it is possible for the text not to be printed.

2\. strcpy/memcpy is not safe when the memory regions overlap since it might
do optimizations that corrupt the string[2]. memmove is the right function to
use.

3\. Running the process with a non-writable working directory would raise an
exception when trying to open the file for appending or create the file.
Additionally, the example mentions web developers, so it probably means
multiple threads/processes executing concurrently. There is no synchronization
in place, so output might be corrupted (small writes might behave atomically,
but larger ones will most likely be written in chunks, which introduces
races).

[1]:
[http://stackoverflow.com/a/4201325/688337](http://stackoverflow.com/a/4201325/688337)

[2]: [http://linux.die.net/man/3/strcpy](http://linux.die.net/man/3/strcpy)

edit: formatting.

~~~
Someone
The third one also needs a try/finally to guarantee (for some definition of
the word) that the file gets closed.

With the code as-is, you can introduce errors elsewhere in the program by
renaming the log file, creating a directory with the same name, waiting a
while for the process to run out of file descriptors, and then deleting the
directory and undoing the rename. Conversely, the function can throw if
another part of the program keeps too many files open.

------
xendo
All live tdd pair programming presentations that I've seen were essentially
examples of structured programming by coincidence.

