Walking the Line

    Brandon Rhodes
    Opening Keynote
    PyTexas 2023 • Austin
An example
An example
 Let’s start writing a quick Python script.
An example
 Let’s start writing a quick Python script.
  Given a sample program, consider editing it —
  adding or deleting one character.  How many of
  all the possible edits produce valid code?
An example
 Q: What sample program should the script edit?
An example
 Q: What sample program should the script edit?
 A: Have the program edit its own code!
An example
 
text = open(__file__).read()
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
Then, I stop.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
When I’m ready to move from one thought to the next, I have a habit: quickly checking my code so far.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
I pause for ½ second.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
I pause for ½ second. This triggers Emacs to syntax-check the script.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
I pause for ½ second. This triggers Emacs to syntax-check the script. The result?
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
I pause for ½ second. This triggers Emacs to syntax-check the script. The result? No errors.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. Saving kicks off an automatic run of the script over in my terminal window.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In the old days, this was often not worth it, because it would have meant polling.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In the old days, this was often not worth it, because it would have meant polling. Polling is expensive when there are no edits.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In the old days, this was often not worth it, because it would have meant polling. Polling is expensive when there are no edits. Polling is slow when there are edits.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. But today, Linux offers `inotify`.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. But today, Linux offers `inotify`. It lets a process sleep waiting for changes to a file.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In my `homedir` GitHub repo:
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In my `homedir` GitHub repo:
,watch python example.py -- **/*.py
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
So I hit Save. In my `homedir` GitHub repo:
,watch python example.py -- **/*.py
,w example.py
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
Thanks to that automation, I hit Save, and can glance over at my terminal window to see the output — in this case, it worked!
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
print(deletes)
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
for code in deletes:
    eval(code)
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
for code in deletes:
    eval(code)
Expectation: syntax error.
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
for code in deletes:
    eval(code)
Expectation: syntax error. So, I hit Save.
An example
 
Traceback (most recent call last):
  File "<string>", line 1
    ext = open(__file__).read()
        ^
SyntaxError: invalid syntax
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
for code in deletes:
    eval(code)
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
counts = {'syntax': 0, 'ok': 0}
for code in deletes:
    try:
        eval(code)
    except SyntaxError:
        counts['syntax'] += 1
    else:
        counts['ok'] += 1
print(counts)
An example
 And, I hit Save.
An example
 And, I hit Save.
  
{'syntax': 811, 'ok': 0}
An example
 And, I hit Save.
  
{'syntax': 811, 'ok': 0}
Drat.
An example
 So I briefly comment out the `try…except`.
An example
 So I briefly comment out the `try…except`.
 And hit Save.
An example
 
Traceback (most recent call last):
  File "<string>", line 1
    ext = open(__file__).read()
        ^
SyntaxError: invalid syntax
An example
 
Traceback (most recent call last):
  File "<string>", line 1
    ext = open(__file__).read()
        ^
SyntaxError: invalid syntax
Recall that the first line of the file is:
An example
 
Traceback (most recent call last):
  File "<string>", line 1
    ext = open(__file__).read()
        ^
SyntaxError: invalid syntax
Recall that the first line of the file is:
text = open(__file__).read()
An example
 
pydoc eval
An example
 
pydoc eval
‘The source may be a string representing a Python expression or a code object as returned by compile().’
An example
 
Traceback (most recent call last):
  File "<string>", line 1
    ext = open(__file__).read()
        ^
SyntaxError: invalid syntax
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
counts = {'syntax': 0, 'ok': 0}
for code in deletes:
    try:
        eval(code)
    except SyntaxError:
        counts['syntax'] += 1
    else:
        counts['ok'] += 1
print(counts)
An example
 
text = open(__file__).read()
r = range(len(text))
deletes = [text[:i] + text[i+1:] for i in r]
counts = {'syntax': 0, 'ok': 0}
for code in deletes:
    try:
        compile(code, 'src', 'exec')
    except SyntaxError:
        counts['syntax'] += 1
    else:
        counts['ok'] += 1
print(counts)
An example
 And I hit Save.
An example
 And I hit Save.
  
{'syntax': 107, 'ok': 191}
An example
 And I hit Save.
  
{'syntax': 107, 'ok': 191}
Success!
An example
 Let’s improve that diagram.  My mistake with `eval()`
An example
 didn’t feel like progress — it felt like a detour.
An example
 didn’t feel like progress — it felt like a detour.
 
 When my code breaks, I feel like I have
 crossed an invisible line that separates
 all working programs from all broken ones.
Hence the title of this talk —
‘Walking the line’
‘Walking the line’
 So let’s discuss the two states, red and green.
Green.
Green.
 Developers ♥ green.
Green.
 Developers ♥ green.
  
  Green leaves us free to make more progress.
Green.
 Developers ♥ green.
  
  Green leaves us free to make more progress.
  
  Red leaves us stuck until it’s fixed.
Green.
 Developers ♥ green.
  
  Green leaves us free to make more progress.
  
  Red leaves us stuck until it’s fixed.
  
  And coding for too long without stopping to detect
  the difference can leave us stranded.
Green.
 All other things being equal, fixing code early is cheap,
 while fixing code later is expensive.
Green.
 All other things being equal, fixing code early is cheap,
 while fixing code later is expensive.
 
 Q: So, why would you write lots of code at once?
Green.
 All other things being equal, fixing code early is cheap,
 while fixing code later is expensive.
 
 Q: So, why would you write lots of code at once?
 A: Side quests.
Green.
 All other things being equal, fixing code early is cheap,
 while fixing code later is expensive.
 
 Q: So, why would you write lots of code at once?
 A: Side quests.
 
 You discover the change you are making requires other
 changes, that themselves each require further changes.
Green.
 
 You can wind up laboring over a huge diff
 that tries to change everything at once.
Green.
 
 You can wind up laboring over a huge diff
 that tries to change everything at once.
 
 Instead, when I see a side quest, I `git stash` what
 I’m working on and solve the side quest first.
Green.
 @KentBeck
Green.
 @KentBeck
  ‘make the change easy, then make the easy change’
Green.
 @KentBeck
  Few people remember: that’s not the full tweet.
Green.
 @KentBeck
  ‘for each desired change,
   make the change easy (warning: this may be hard),
   then make the easy change’
Green.
 The side quests will not necessarily be easy.
Green.
 But at least you get to tackle one quest at a time,
 and get natural stopping points at which you
 can commit your work.
Green.
 But at least you get to tackle one quest at a time,
 and get natural stopping points at which you
 can commit your work.
 
 But we always want green, yeah, TODO.
Green.
Red.
Red.
 Do we programmers ever starting wanting to break things?
Red.
 Yes.
Red.
 One case is TDD — Test-Driven Development.

Red.
 One case is TDD — Test-Driven Development.

  It would be a whole talk of its own.
Red.
 One case is TDD — Test-Driven Development.

  1. Start by writing a failing test of a new feature.
Red.
 One case is TDD — Test-Driven Development.

  1. Start by writing a failing test of a new feature.
  2. Then write the code to turn the test green.
Red.
 One case is TDD — Test-Driven Development.

  1. Start by writing a failing test of a new feature.
  2. Then write the code to turn the test green.
  
  • Gets the worst thing, writing the test, over with first.
Red.
 One case is TDD — Test-Driven Development.

  1. Start by writing a failing test of a new feature.
  2. Then write the code to turn the test green.
  
  • Gets the worst thing, writing the test, over with first.
  • Arranges a nice instant reward for when you finish.
Red.
 One case is TDD — Test-Driven Development.

  1. Start by writing a failing test of a new feature.
  2. Then write the code to turn the test green.
  
  • Gets the worst thing, writing the test, over with first.
  • Arranges a nice instant reward for when you finish.
  • By its nature produces a decently well-tested codebase.
Red.
 One case is TDD — Test-Driven Development.

  For the purposes of this talk, let’s just note
  that TDD leverages your usual negative emotions
  about Red: you want the Red to go away, so you
  write code that satisfies the new test.
Red.
 But it is the claim of this talk that
 emotions can also work the other way round —

Red.
 But it is the claim of this talk that
 emotions can also work the other way round —

 it is the claim of this talk that there are
 situations where you aren’t avoiding Red,
Red.
 But it is the claim of this talk that
 emotions can also work the other way round —

 it is the claim of this talk that there are
 situations where you aren’t avoiding Red,
 you’re seeking it out.
What could lead to that?
Feelings of insecurity.
Feelings of insecurity.
 Let’s consider three.
Feelings of insecurity.
 #1
Feelings of insecurity.
 Too much green.
Feelings of insecurity.
 Imagine that you’re performing extensive refactoring.
Feelings of insecurity.
 Imagine that you’re performing extensive refactoring.
 Each time you Save, you’re thrilled the result is still Green.
Feelings of insecurity.
 Your streak of green runs longer and longer.
Feelings of insecurity.
 Your streak of green runs longer and longer.
 Until—
Feelings of insecurity.
 Until—you become a tiny bit incredulous.
Feelings of insecurity.
 Until—you become a tiny bit incredulous.
 ‘Am I really this good at refactoring?’
Feelings of insecurity.
 Until—you become a tiny bit incredulous.
 ‘Am I really this good at refactoring?’
 
 ‘Hey — wait — the tests are running, right? Right?’
Feelings of insecurity.
 Until—you become a tiny bit incredulous.
 ‘Am I really this good at refactoring?’
 
 ‘Hey — wait — the tests are running, right? Right?’
 
 taps mic
Feelings of insecurity.
 Until—you become a tiny bit incredulous.
 ‘Am I really this good at refactoring?’
 
 ‘Hey — wait — the tests are running, right? Right?’
 
 taps mic
 Is this on?
Feelings of insecurity.
 So I invoke a maneuver: my `asdf` check!
Feelings of insecurity.
 
C-e RET a s d f C-x s
Feelings of insecurity.
 
asdf
Feelings of insecurity.
 
asdf
And then I hit Save.
Feelings of insecurity.
 
asdf
If all is well, then I will see the happy result:
Feelings of insecurity.
 
asdf
If all is well, then I will see the happy result:
NameError: name 'asdf' is not defined
Feelings of insecurity.
 
asdf
If all is well, then I will see the happy result:
NameError: name 'asdf' is not defined
—and I will keep coding.
Feelings of insecurity.
 
asdf
If all is well, then I will see the happy result:
NameError: name 'asdf' is not defined
But what if I don’t see it?
Feelings of insecurity.
 But what could have gone wrong?
Feelings of insecurity.
 But what could have gone wrong?
  Among the possibilities are —
Feelings of insecurity.
 But what could have gone wrong?
  1. You’ve been editing the wrong function!
     Turns out? There are two named `user_delete()`.
Feelings of insecurity.
 But what could have gone wrong?
  2. You need to hit Shift-Reload, not plain Reload, for
     your browser to see that edit to the CSS.
Feelings of insecurity.
 But what could have gone wrong?
  3. The repository is checked out twice on your laptop
     and you are editing one repo while running the other.
Feelings of insecurity.
 But what could have gone wrong?
  4. Your editor’s jump-to-definition shortcut decided
     to jump into `site-packages/` instead of the repository.
Feelings of insecurity.
 But what could have gone wrong?
  4. Your editor’s jump-to-definition shortcut decided
     to jump into `site-packages/` instead of the repository.
   (So, you’re editing a pip-installed copy of the code)
Feelings of insecurity.
 But what could have gone wrong?
  4. Your editor’s jump-to-definition shortcut decided
     to jump into `site-packages/` instead of the repository.
   (Bonus points if you forget to remove `asdf` and, months
    later, you run some other Python application and see—)
Feelings of insecurity.
 But what could have gone wrong?
  4. Your editor’s jump-to-definition shortcut decided
     to jump into `site-packages/` instead of the repository.
   (Bonus points if you forget to remove `asdf` and, months
    later, you run some other Python application and see—)
    
NameError: name 'asdf' is not defined
Feelings of insecurity.
 But what could have gone wrong?
  5. You are editing local code, on your laptop,
     but reloading a remote version of the site.
Feelings of insecurity.
 But what could have gone wrong?
  5. You are editing local code, on your laptop,
     but reloading a remote version of the site.
   
http://localhost/shopping-cart
Feelings of insecurity.
 But what could have gone wrong?
  5. You are editing local code, on your laptop,
     but reloading a remote version of the site.
   
https://canary.example.com/shopping-cart
Feelings of insecurity.
 But what could have gone wrong?
  6. You did open the remote copy of the file,
     but your `sshfs` connection has gone down,
     and your editor didn’t tell you.
Feelings of insecurity.
 But what could have gone wrong?
  7. A disk error forced your filesystem to remount
    read-only, but your editor isn’t telling you.
Feelings of insecurity.
 But what could have gone wrong?
  7. A disk error forced your filesystem to remount
    read-only, but your editor isn’t telling you.
  
    Why? Because, the cool fancy new color-highlighted
    error-report plugin you installed dies silently on a
    read-only filesystem, and so can’t tell you about it.
Feelings of insecurity.
 But what could have gone wrong?
  Bonus Round!
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   When your boss asked if the app works on leap day?
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   When your boss asked if the app works on leap day?
   So you set your system clock briefly to next February 29,
   hit Restart, and Reload to show that the app still worked.
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   Turns out?
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   Turns out?
   All the `*.pyc` files written during those five minutes
   are dated 2024 February 29 — which is in the future.
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   Turns out?
   All the `*.pyc` files written during those five minutes
   are dated 2024 February 29 — which is in the future.
   
   Which makes those `*.pyc` files more recent than anything
   you save today, so Python is ignoring your new `*.py` edits.
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   
   Quick advice: turn off `*.pyc`.
Feelings of insecurity.
 But what could have gone wrong?
  8. Remember the end of the day last Friday?
   
   Quick advice: turn off `*.pyc`.
    
/home/brandon/.bashenv:
export PYTHONDONTWRITEBYTECODE=PLEASE
Feelings of insecurity.
 Any of these underlying causes
 will make you want to cross the line.
Feelings of insecurity.
 You will only be happy again once you’re assured
 that the line is back in place, and once again ready
 to hit you with consequences as you write code.
Feelings of insecurity.
Feelings of insecurity.
 #2
Feelings of insecurity.
 My tests are running. But are they protecting me?
Feelings of insecurity.
 My tests are running. But are they protecting me?
  Especially important question before a big refactor.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
pandas
df.clip_upper(...)
df.clip_lower(...)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
clip_upper = min
clip_lower = max
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Q: How do we know whether our tests cover both cases?
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Q: How do we know whether our tests cover both cases? A: Let’s cross the line!
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Let’s do quick edits to remove `max()`.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Let’s do quick edits to remove `max()`.
capital_gain = line_16
# or
capital_gain = -3000
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Let’s do quick edits to remove `max()`. Hint: don’t experiment on the line itself!
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Let’s do quick edits to remove `max()`. Hint: don’t experiment on the line itself! Make a copy.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
capital_gain = max(line_16, -3000)
capital_gain = max(line_16, -3000)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = max(line_16, -3000)
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = max(line_16, -3000)
M-m '#' C-k C-y RET C-y
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = max(line_16, -3000)
No we’re free to break the code.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = line_16
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit. PEP-8 ‘Each line of a block comment starts with a # and a single space’
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit. PEP-8 ‘Inline comments…should start with a # and a single space’
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit. 2. Makes it easy to undo.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
1. Use `#` with no space, so you’ll remember not to commit. 2. Makes it easy to undo. 3. Lets you, as you edit, compare to the original.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
Don’t leave feelings of insecurity unacknowledged.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
#capital_gain = max(line_16, -3000)
capital_gain = -3000
Don’t leave feelings of insecurity unacknowledged. Go ahead and quickly break the code, to be sure that you are safely fenced in by your tests.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  Breaking things.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  Breaking things.
  
  In real life — in a workplace, in a relationship — crossing
  a line just to see if it’s really there is an anti-pattern.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  So we must balance two philosophies.
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
  Gandalf: ‘He that breaks a thing to find out what it is
  has left the path of wisdom.’
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
  Gandalf: ‘He that breaks a thing to find out what it is
  has left the path of wisdom.’
  
  Blake: ‘You never know what is enough
Feelings of insecurity.
 My tests are running. But are they protecting me?
  
  Gandalf: ‘He that breaks a thing to find out what it is
  has left the path of wisdom.’
  
  Blake: ‘You never know what is enough
  unless you know what is more than enough.’
Feelings of insecurity.
 
Feelings of insecurity.
 #3
Feelings of insecurity.
 Some of us are haunted by an insecurity—
Feelings of insecurity.
 ‘Wait! Maybe this code could be even simpler.’
Feelings of insecurity.
 ‘Wait! Maybe this code could be even simpler.’
  
  Some of us suffer from a nagging fear that we might have
  written slightly more code than is strictly necessary.
Feelings of insecurity.
 ‘Wait! Maybe this code could be even simpler.’
  
  Some of us suffer from a nagging fear that we might have
  written slightly more code than is strictly necessary.
  
  Surprisingly, this can lead to non-optimal outcomes.
Feelings of insecurity.
 A story.
Feelings of insecurity.
 I once worked in Georgia Tech’s IT shop.
Feelings of insecurity.
 Every night, our script would run `scp` to copy a CSV
 from Human Resources that listed all 30k employees.
Feelings of insecurity.
 Every night, our script would run `scp` to copy a CSV
 from Human Resources that listed all 30k employees.
 
 Any of yesterday’s employees that were missing
 from today’s list received a goodbye email.
Feelings of insecurity.
 
 Q: Guess what happened early one Monday morning when
 the TCP connection died 3k lines before the end?
Feelings of insecurity.
 
 Q: Guess what happened early one Monday morning when
 the TCP connection died 3k lines before the end?
 
 A: 3k employees were emailed ‘goodbye’.
Feelings of insecurity.
 
 Q: Guess what happened early one Monday morning when
 the TCP connection died 3k lines before the end?
 
 A: 3k employees were emailed ‘goodbye’.
 
 It was a bad day.
Feelings of insecurity.
 What interests me now, looking back, is my inadequate
 response to the problem.
Feelings of insecurity.
 
 What did we developers do?
Feelings of insecurity.
 
 What did we developers do?
 We read some docs, did some tests, and switched
 from `scp` to `rsync`.
Feelings of insecurity.
 
 What did we developers do?
 We read some docs, did some tests, and switched
 from `scp` to `rsync`.
 Done! Problem solved!
Feelings of insecurity.
 
 What did we developers do?
 We read some docs, did some tests, and switched
 from `scp` to `rsync`.
 Done! Problem solved!
 Right?
Feelings of insecurity.
 Well, we were then called into a meeting.
Feelings of insecurity.
 Well, we were then called into a meeting.
  With a VP.
Feelings of insecurity.
 Well, we were then called into a meeting.
  With a VP.
  He ordered that HR append an `--END--` line to the file
  and that we must check for it before using it.
Feelings of insecurity.
 Of course, we were enraged.
Feelings of insecurity.
 Of course, we were enraged.
 
 1. We had already solved the problem. Ourselves!
Feelings of insecurity.
 Of course, we were enraged.
 
 1. We had already solved the problem. Ourselves!
 2. The design of `rsync` made an extra check illogical.
Feelings of insecurity.
 Of course, we were enraged.
 
 1. We had already solved the problem. Ourselves!
 2. The design of `rsync` made an extra check illogical.
 3. The beautiful symmetry of the CSV file — every line,
     the same number of commas, every line, the same number
     of fields — is ruined by adding the extra line.
Feelings of insecurity.
 Of course, we were enraged.
 
 1. We had already solved the problem. Ourselves!
 2. The design of `rsync` made an extra check illogical.
 3. The beautiful symmetry of the CSV file — every line,
     the same number of commas, every line, the same number
     of fields — is ruined by adding the extra line.
 
 So we wound up arguing — unsuccessfully — against
 what strikes me today as an eminently sensible idea.
Feelings of insecurity.
 What went wrong?
Feelings of insecurity.
 What went wrong?
 
 We were indulging in the pleasures of the mathematician
Feelings of insecurity.
 What went wrong?
 
 We were indulging in the pleasures of the mathematician
 we wanted code to be minimal and concise, like a proof —
Feelings of insecurity.
 What went wrong?
 
 We were indulging in the pleasures of the mathematician
 we wanted code to be minimal and concise, like a proof —
 when our role demanded the maturity of an engineer.
Feelings of insecurity.
 Do engineers walk right against the line
 between success and failure?
Feelings of insecurity.
 Do engineers walk right against the line
 between success and failure?
 
 Do they build bridges that can support the rated
 limit but not a single ton more?
Feelings of insecurity.
 Do engineers walk right against the line
 between success and failure?
 
 Do they build bridges that can support the rated
 limit but not a single ton more?
 
 No.
Feelings of insecurity.
 Do engineers walk right against the line
 between success and failure?
 
 Do they build bridges that can support the rated
 limit but not a single ton more?
 
 No.
 
 Systems that are robust
Feelings of insecurity.
 Do engineers walk right against the line
 between success and failure?
 
 Do they build bridges that can support the rated
 limit but not a single ton more?
 
 No.
 
 Systems that are robust
 are also, necessarily, redundant.
Feelings of insecurity.
 
 I ought to have welcomed the chance to give the
 machine two reasons to not disable 3k employee
 accounts on a Monday morning.
Feelings of insecurity.
 
 I ought to have welcomed the chance to give the
 machine two reasons to not disable 3k employee
 accounts on a Monday morning.
 
 I ought to have been happy to take a deep breath and
 step away from the line for once.
In conclusion—
In conclusion—
 As a programmer, I risk being driven by my emotions
In conclusion—
 As a programmer, I risk being driven by my emotions
 as if by a storm at sea.
In conclusion—
 As a programmer, I risk being driven by my emotions
 as if by a storm at sea.
 
 They can lead me to a comfortable harbor of
 double-checked code, cosseted amongst many tests.
In conclusion—
 As a programmer, I risk being driven by my emotions
 as if by a storm at sea.
 
 They can lead me to a comfortable harbor of
 double-checked code, cosseted amongst many tests.
 
 Or they can lead me to overly fragile systems
 that bring my customers to shipwreck.
In conclusion—
 Even as we are typing our code, we ride a knife’s edge
In conclusion—
 Even as we are typing our code, we ride a knife’s edge
 
 between code that fails or runs,
In conclusion—
 Even as we are typing our code, we ride a knife’s edge
 
 between code that fails or runs,
 tests that are spotty or thorough,
In conclusion—
 Even as we are typing our code, we ride a knife’s edge
 
 between code that fails or runs,
 tests that are spotty or thorough,
 systems that are fragile or robust.
In conclusion—
 
In conclusion—
 
 Whether we strain to stay on one side of line,
In conclusion—
 
 Whether we strain to stay on one side of line,
 or are compulsive in probing and stepping across,
In conclusion—
 
 Whether we strain to stay on one side of line,
 or are compulsive in probing and stepping across,
 it’s always there, right alongside us.
In conclusion—
 
 Whether we strain to stay on one side of line,
 or are compulsive in probing and stepping across,
 it’s always there, right alongside us.
 
 For by the very nature of our art,
In conclusion—
 
 Whether we strain to stay on one side of line,
 or are compulsive in probing and stepping across,
 it’s always there, right alongside us.
 
 For by the very nature of our art,
 we programmers always, always, always
In conclusion—
 
 Whether we strain to stay on one side of line,
 or are compulsive in probing and stepping across,
 it’s always there, right alongside us.
 
 For by the very nature of our art,
 we programmers always, always, always
 are walking the line.


@brandon_rhodes

@brandon_rhodes@mastodon.social

https://rhodesmill.org/brandon/