Notes
Continueing my book club notes on the the Pragmatic Programmer by Andrew Hunt
and David Thomas.
Chapter 3
- The best way to store knowledge is in plain text, not binary blobs
- The drawback to plain text is that it comrpesses poorly, and may be expensive
to process. This doesn’t seem particularly relevant with modern computers, but
I suppose embeded systems still suffer this drawback.
- Plaintext helps insure against obsolescence and eases testing
- Shell beats GUI
- Get good at one editor until it’s like playing the piano
- Use source control (yeah we’re doing the obvious now)
- Embrace debugging as just another form of problem solving
- Turn your ego off when debugging. This is made possible by focusing on fixing
the problem and not assigning blame
- Avoid panicing when debugging, accept that the bug is possible, resist the
urge to fix the symptoms while leaving the cause
- You can only get so far with automated testing, at times its fastest to simply
interview the user
- Rubber Ducking, attempt to explain the problem to someone else, if it’s a
rubber duck
- Learn a scripting language (or these days, learn a systems language)
- Have code generators actively monitor and rebuild generated code
Chapter 4
- We are taught to code defensively and validate against bad input. We should
also be defensive against ourselves
- Design by Contract (DBC). Define a contract of the pre-conditions for a method
call and the guranteed post conditions that method promises. Contstraint
invariantss to occuring only within the call itself.
- Be strict with the pre-conditions and make only small promises for the post
conditions
- Crash early, don’t ignore an error or assume the system will resume stability
once one occurs
- Use assertions to guarantee that events that won’t happen can’t happen. Keep
assertions in production to detect these “impossible” events during operation
(you won’t detect these during a normal test pass anyway)
- Exceptions should rarely be used as they interupt the program flow.
- If you remove all the exception handlers, then the code should crash.
- The routine that allocates a resource is responsible for deallocating it.
Continuing my deep dive into shell and editor commands to find, useful tools
that I’m not taking full advantage. This week is the Find and Tree commands.
Find
Find is used for traversing a tree of files and performing some operation on
them. It is one of the core Unix utilities and fairly universal on systems. My
big discovery this time is the realization that I can use find for more than
just searching for files. I can use find to perform operations on the results.
There are multiple actions besides the default -print
, e.g. -delete
and
-exec
both open up a world of possiblities that I would have otherwise
resorted to piping the results into a loop (or resorted to Python) to resolve.
$ find [-H] [-L] [-P] path... [expression]
The flag -P
is the default behavior. -L
specifies find
to follow symbolic
links. -H
will follow symbolic links while procesing the command line
arguments. The path
parameter is required and can use globbing similar to ls
or other terminal commands.
find
excepts a number of operators for fine tuning our search. ( expr )
forces precedence, ! expr
evaluates true if the expression is false, expr1 -a
expr2
evaluates expr2
only if expr1
is true, expr1 -o expr2
evaluates as
true if either expression is true. For example:
find . -name 'fileA*' -o -name 'fileB*'
Searches the current working tree for a file whose names start with “fileA” or
“fileB.”
Example commands:
$ find . -name 'my*' |
Searches working directory for files starting with “my” |
$ find . -name 'my*' -type f |
As above, but excludes directories and searches only for “regular files” |
$ find . -name 'my*' -type f -ls |
As above, but pipes the results into the ls command. |
$ find . ../Done -name 'my*' -type f -ls' |
As above, but this time we are searching both the working directory and the ../Done directory! |
$ find . -name '*md' -o -name '*latex' |
Find all markdown or latex files in the working directory |
$ find . -name '*.md' -type f -exec chmod 664 {} \; |
Finds all markdown files in the working directory and executes chmod 664 replacing the value {} with the path to the file. Note the required \; at the end of the command and that the command cannot be placed in quotes. |
$ find . -type d -empty -delete |
Deletes all empty directories in the working directory. Note, that the delete option can simply be used as a replacement for the default option of -print . That is, whatever whould hae been returned without the -delete is what would be deleted. |
$ find . -iname "*MD" |
Case insensitive name search |
$ find . -size 100k -a -size -500k |
Find allows for searching by file size. |
$ find . -mtime 30 |
Find all files modified in the last month. We can do -mtime +50 -mtime -100 to find files modified more than 50 days ago and less than 100 days. |
$ find . -cmin -60 |
Find all files modified in the last hour. find . -mmin -1 does the same thing but with an interval of hours. |
Tree
While reading on find
last week, I stumbled upon tree
. Tree
is one of
those commands that I ocassionally recall, think is really cool. Then completely
forget about.
Tree gives you the ability to generate a visualization of the directory tree,
much like the old Windows Explorer provided a tree view of your directory.
In simplest usage, you simple call tree
, and it outputs a tree representation
of the current working directory. If we want to display a different directory,
we can provide that for the first argument: tree ~/Documents
.
By default, tree
displays symbolic links showing where they point towards.
However, if the link is a directory, it does not, by default recurse into that
directory.
Flags:
-a |
Display hidden files |
-d |
List directories only |
-f |
Display full paths |
-i |
Don’t indent/show tree lines. Use in conjunction with -f to create a file list |
-l |
That is a lowercase “L,” do recurse into symbolic directories |
-P pattern or -I pattern |
List files that match the pattern, or list files that don’t match the pattern |
-u , -g , -p , -s , -h |
Print the user, group, permissions, size in bytes, or human-readable sizes |
Recently, I’ve been running a book club to cover the contents of the Pragmatic
Programmer by Andrew Hunt and David Thomas. One of those volumes that has been
held up, forever, as a text that any good Software Engineer should have read.
The text itself is rather sound, although starting to show it’s age in the era
of software-on-the-browser.
Probably not going to do much of an articulated look at the book. Rather, I
think I will simply post my cliff notes as I, or we go through each chapter.
Chapter 1
- Take responsibility for actions, career advancement, project, and day to day
work while being honest about your shortcomings
- Understand that responsiblity is something you actively agree to.
- You don’t directly have control over the success of things that you take
responsibility for.
- You do have the right to not take responsiblity for an impossible task, but
if you do accept responsiblity, then you are accountable for it’s success
- Provide options, avoid lame excuses
- “Broken windows” in the code encourage the code base to degrade over time
- “Start up fatgue” sets in when you make a greenfield request too big. Start
small, and work to larger
- Keep the big picture in mind to avoid creep
- Software needs to be good enough for the the user; the maintainer, and lastly;
for yourself
- Ask the users how good they want their software to be
- Great software today is often preferable to perfect software tomorrow
- Know when to stop making improvements
- Your “Knowledge Portfolio” is your most important asset, but it has a
half-life. Invest in it regularly, manage high and low risk skillsets, and
keep a diverse portfolio
- Some ideas to keep on top: learn a new language each year (particularly ones
that introduce new paradigms), read a technical book each quarter, read
non-technical books, participate in user groups and conferences, stay current
on industry trends
- You’re communicating only if you’re conveying information. Know what you want
to say, know your audience, choose your moment, choose a style, make it look
good, involve your audience, be a listener, and get back to people
Chapter 2
- We are always maintaining software, it is a routine part of the development
process
- “Every piece of knowledge must have a single, unambiguous, authoritative
representation within a system” (DRY)
- Wrong comments are worse than no comment at all
- Keep code orthogonal, that is eliminate dependencies such that internal
changes in a module do not change the external interface.
- Orthogonal code reduces risk by isolating bad code, allowing for agile
development, easing tests, and reducing vendor lock-in
- Avoid excessive team overlap
- When designing architecture, ask if you were to dramatically change the
requirements how many modules must then change?
- Develop shy code that doesn’t reveal it’s internal impelementations
- Avoid global data
- Use the stategy pattern to avoid functions with similar bodies
- Do not approach projects with notion that there is only one way to do it
- When a critical decision is made it narrows the pool of future possible
decisions; put off such critical decisions until later by making code
reversable
- “Tracer Bullets:” start with a small, single aspect of the final system and
complete the piece first. Iterate upon that piece to fully flesh out the
system. Integrate daily, rather than building a single monolith and then
integrating
- Prototyping generates disposable code. Stakeholders must be made plainly aware
that a prototype is not a finished product and cannot contribute to the final
production product
- Prototypes are creates to analyze and expose risk. They are designed to
explore a single aspect of the software
- Use prototypes for things that haven’t been tried before, critical systems,
and anything unproven or experimental
- The language of the domain suggests the programming solution
- You can implement the domain language into an interpretive language
- When estimating use the units of the quote to suggest uncertainty, e.g. a
project doable in a sprint is quoted in days, a project doable in a month or
two in weeks, a project doable in over a quarter, in months, etc.
Continuing my deep dive into shell and editor commands to find, useful tools
that I’m not taking full advantage. This week is debugging PHP using Vim and
XDEBUG.
XDebug in Vim
XDebug has been installed on every development machine that I’ve worked on for
as long as I’ve worked. It outputs wonderfully formatted stacktraces and
var_dump
values. However, the interactive debugger side of XDebug remains
little used due to the overhead of setting it up.
When I developed using PHPStorm, the interactive debugger seemed extraordinarily
unstable. After taking the time to set up a project, map the directories
correctly, configure ports and then trigger the debugger it would run for a few
lines then halt. I eventually stopped using it.
The Vim VDebug plugin, running locally on the server seems a much more
stable implementation. However, I still use it much less often then I should.
Largely, this is due to comfort level. I’m not comfortable enough with it, so I
don’t bother triggering it.
Yet, it would be easy to become comfortable. Any time that I want to inspect the
value of a variable under an operations, instead of echoing or var_dump
ing
that value out – put a breakpoint, and trigger the debugger. After a while, it
will become like second nature to enter the debugger instead of printing the
variable. Consequentially, if after inspecting the first variable, I discover
the need to inspect a second variable, well the debugger has already started and
inspecting the second variable is a zero-cost operation.
Installing and configuring XDebug, I leave to the documentation. Initiating the
interactive debugger is done through VDebug, a Vim plugin that works with PHP,
Python, Ruby, Perl, and NodeJS debuggers – or as it’s documentation says, any
debugger that implements the DBGp standard.
Starting the XDebug Plugin:
Debugging starts by selecting a breakpoint line, navigating to it and pressing
<F10>
to toggle the breakpoint. Second, we start the debugging session by
pressing <F5>
. We then have 30 seconds to start the session which can be done
in one or two ways.
If accessing our PHP script via the browser, we add
XDEBUG_SESSION_START=$ideKey
to the URL query string. If accessing our script
via the commadn line, we start the script via:
php -dxdebug.remote_enable=1 \
-dxdebug.remote_autostart=On \
-dxdebug.idekey=$ideKey \
-dxdebug.remote_port=9000 \
some-script.php
Where $ideKey
by convention is the unix username and port is 9000 or whatever
port XDebug was configured to use.
Debug controls:
<F5> |
Run to next breakpoint/end of script |
<F2> |
Step over a function or method call |
<F3> |
Step into a function or method call |
<F4> |
Step out of a function or method call |
<F6> |
Terminate script |
<F7> |
Detach from script, run to it’s normal end |
<F9> |
Run to cursor |
<F10> |
Toggle breakpoint |
<F12> |
Evaluate variable under cursor |
When to Step Over, Into, or Out
The step over, into, and out of always tricks me up.
First, contrary to what I thought, you can’t use step-over to step over loops.
They only effect whether you are stepping into, over, and out of function or
method calls. Essentially, if a function is to be called on the line under
evaluation if you step-into it, then we step into the function and debug line by
line that function. If we step-over it, then the debugger executes the function
and takes us to the next line in the current context. Step-out is used if we
first stepped into a function and now want to simply execute to the end of that
function and step back up to the calling context.
One of my professional goals this year is to make a marked improvement on my
shell (zsh) and editor (vim) skills. I know enough commands to get me through
the work day, yet every time I see a real shell or vim poweruser go to town, I am
reminded that I am probably only confident in 10% of the commands that I could
be using.
Every now and then, I’ll force myself to use h, j, k, l instead of ←, ↑, ↓, → to
navigate in Vim and my Tmux panels. The skill lasts about a week before I’m back
to the arrow keys and mouse. Every now and then, I’ll try to expand beyond ls
,
grep
, cd
, cp
, mkdir
, mv
, rm
, pwd
, clear
and cat
, in the shell.
I always rebound after a couple weeks, because I can get 90% done with those
nine programs.
Hence, a series of articles summarizing the man pages for different
applications. A process that hopefully sees me making more regular use of them.
Less
My typical solution to navigating a read-only text file is clear && cat
$filename
and then scrollback with the mouse or to pipe. My solution to log
files are to pipe them into text files and open them in Vim (not recommended on
memory-limited systems). So my first Unix command for the year is less
, that
wonderful program that I get dumped into by Git all the time.
Less is a program that outputs a text file or stream with a buffer to display
either more of the stream or page up to previously output lines.
Less gives me something that I’ve been trying, incorrectly to do with clear &&
cat
all along: display a buffer of just the text file. With my old solution, on
long file outputs, it was easy to scroll up past the start of the file and into
old commands. With Less, this isn’t a problem. The buffer starts and ends with
the contents of the text file. Likewise, I frequently output formatted excerpts
from my Todo List using the XP and LSGP/LSGC add-ons and
pipe them into text files or open new terminals to have a clean buffer to scroll
back on. Less solves this by outputing the multi-screen-height output into a
single buffer.
Useful Commands/Options
SPACE |
Scroll down a screen. |
RETURN |
Scroll down one line. |
d |
Scroll down half a page. |
u |
Scroll up half a page |
y |
Scroll up one line. |
F |
Scroll to the end then tail the stream. |
g |
Go to line N |
G |
Go to end of file. |
m + Char |
Marks position with that letter. |
’ + Char |
Return to marked position. |
/ |
Search forward |
? |
Search backwards |
@ |
Search for pattern in file |
n |
Repeat search to next result |
N |
Repeat serach to previous result |
t |
Go to next tag |
T |
Go to previous tag |
v |
Open file in Vim |
! + Cmd |
Invokes shell from inside less |
-R |
Terminal colors maintained |
-N |
Show line numbers |
-JM |
Status columns |