Git's rejected push error
On Saturday I posted
an article explaining how remote branches and remote-tracking branches work in Git.
That article is a prerequisite for this one. But here's the quick
summary:
When dealing with a branch (say, master) copied from a remote
repository (say, origin), there are three branches one must
consider:
The copy of master in the local repository
The copy of master in the remote repository
The local branch origin/master that records the last known
position of the remote branch
Branch 3 is known as a “remote-tracking branch”. This is because it
tracks the remote branch, not because it is itself a remote branch.
Actually it is a local copy of the remote branch. From now on I will
just call it a “tracking branch”.
The git-fetch command (green) copies branch (2) to (3).
The git-push command (red) copies branch (1) to (2), and incidentally
updates (3) to match the new (2).
The diagram at right summarizes
this.
We will consider the following typical workflow:
- Fetch the remote
master branch and check it out.
- Do some work and commit it on the local
master .
- Push the new work back to the remote.
But step 3 fails, saying something like:
! [rejected] master -> master (fetch first)
error: failed to push some refs to '../remote/'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
In older versions of Git the hint was a little shorter:
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Everyone at some point gets one of these messages, and in my
experience it is one of the most confusing and distressing things for
beginners. It cannot be avoided, worked around, or postponed; it must
be understood and dealt with.
Not everyone gets a clear explanation. (Reading it over, the actual
message seems reasonably clear, but I know many people find it long
and frighting and ignore it. It is tough in cases like this to decide
how to trade off making the message shorter (and perhaps thereby
harder to understand) or longer (and frightening people away). There
may be no good solution. But here we are, and I am going to try to
explain it myself, with pictures.)
In a large project, the remote branch is always moving, as other
people add to it, and they do this without your knowing about it.
Immediately after you do the fetch in step 1 above, the
tracking branch origin/master reflects the state of the
remote branch. Ten seconds later, it may not; someone else may have
come along and put some more commits on the remote branch in the
interval. This is a fundamental reality that new Git users must
internalize.
Typical workflow
We were trying to do this:
- Fetch the remote
master branch and check it out.
- Do some work and commit it on the local
master .
- Push the new work back to the remote.
and the failure occurred in step 3. Let's look at what each of these
operations actually does.
1. Fetch the remote master branch and check it out.
git fetch origin
master git checkout master
The black circles at the top represent some commits that we want to
fetch from the remote repository. The fetch copies them to the local
repository, and the tracking branch origin/master points to
the local copy. Then we check out master and the local branch
master also points to the local copy.
Branch names like master or origin/master are called “refs”. At
this moment all three refs refer to the same commit (although there are
separate copies in the two repositories) and the three branches have
identical contents.
2. Do some work and commit it on the local master .
edit… git add … git
commit … …
The blue dots on the local master branch are your new commits. This
happens entirely inside your local repository and doesn't involve the
remote one at all.
But unbeknownst to you, something else is happening where you can't
see it. Your collaborators or co-workers are doing their own work in
their own repositories, and some of them have published this work to
the remote repository. These commits are represented by the red dots
in the remote repository. They are there, but you don't know it yet because
you haven't looked at the remote repository since they appeared.
3. Push the new work back to the remote.
git push origin master
Here we are trying to push our local master , which means that we are
asking the remote repo to overwrite its master with our local
one. If the remote repo agreed to this, the red commits would be lost
(possibly forever!) and would be completely replaced by the blue
commits. The error message that is the subject of this article is Git
quite properly refusing to fulfill your request:
! [rejected] master -> master (fetch first)
error: failed to push some refs to '../remote/'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Let's read through that slowly:
Updates were rejected because the remote contains work that you do
not have locally.
This refers specifically to the red commits.
This is usually caused by another repository pushing to the same ref.
In this case, the other repository is your co-worker's repo, not shown
in the diagram. They pushed to the same ref (master ) before you did.
You may want to first integrate the remote changes (e.g., 'git pull
...') before pushing again.
This is a little vague. There are many ways one could conceivably
“integrate the remote changes” and not all of them will solve the
problem.
One alternative (which does not integrate the changes) is to use
git push -f . The -f is for “force”, and instructs the remote
repository that you really do want to discard the red commits in favor
of the blue ones. Depending on who owns it and how it is configured,
the remote repository may agree to this and discard the red commits,
or it may refuse. (And if it does agree, the coworker whose commits
you just destroyed may try to feed you poisoned lemonade, so
use -f with caution.)
See the 'Note about fast-forwards' in 'git push --help' for details.
To “fast-forward” the remote ref means that your local branch is a
direct forward extension of the remote branch, containing everything
that the remote branch does, in exactly the same order. If this is the
case, overwriting the remote branch with the local branch is perfectly
safe. Nothing will be lost or changed, because the local branch
contains everything the remote branch already had. The only
change will be the addition of new commits at the end.
There are several ways to construct such a local branch, and choosing
between them depends on many factors including personal preference,
your familiarity with the Git tool set, and the repository owner's
policies. Discussing all of this is outside the scope of the article,
so I'll just use one as an example: We are going to rebase the blue
commits onto the red ones.
4. Refresh the tracking branch.
git fetch origin master
The first thing to do is to copy the red commits into the local repo;
we haven't even seen them yet. We do that as before, with
git-fetch . This updates the
tracking branch with a copy of the remote branch
just as it did in step 1.
If instead of git fetch origin master we did git pull --rebase
origin master , Git would do exactly the same fetch, and then
automatically do a rebase as described in the next section. If we did
git pull origin master without --rebase , it would do exactly the
same fetch, and then instead of a rebase it would do a merge, which I
am not planning to describe. The point to remember is that git pull
is just a convenient way to combine the commands of this section and
the next one, nothing more.
5. Rewrite the local changes.
git rebase origin/master
Now is the moment when we “integrate the remote changes” with our own
changes. One way to do this is git rebase origin/master . This tells
Git to try to construct new commits that are just like the blue ones,
but instead of starting from the last black commit, they will start from the
last red one. (For more details about how this works,
see my talk slides about it.)
There are many alternatives here to rebase , some quite elaborate,
but that is a subject for another article, or several other articles.
If none of the files modified in the blue commits have also been
modified in any of the red commits, there is no issue and everything
proceeds automatically. And if some of the same files are modified,
but only in non-overlapping portions, Git can automatically combine
them. But if some of the files are modified in incompatible ways, the
rebase process will stop in the middle and ask how to proceed, which
is another subject for another article. This article will suppose
that the rebase completed automatically. In this case the blue
commits have been “rebased onto” the red commits, as in the diagram at
right.
The diagram is a bit misleading here: it looks as though those black
and red commits appear in two places in the local repository, once on
the local master branch and once on the tracking branch. They don't.
The two branches share those commits, which are stored only once.
Notice that the command is git rebase origin/master . This is
different in form from git fetch origin master or git push origin
master . Why a slash instead of a space? Because with git-fetch or
git-push , we tell it the name of the remote repo, origin , and the
name of the remote branch we want to fetch or push, master . But
git-rebase operates locally and has no use for the name of a remote
repo. Instead, we give it the name of the branch onto which we want to
rebase the new commits. In this case, the target branch is the
tracking branch origin/master .
6. Try the push again.
git push origin master
We try the exact same git push origin master that failed in step 3,
and this time it succeeds, because this time the operation is a
“fast-forward”. Before, our blue commits would have replaced the red
commits. But our rewritten local branch does not have that problem: it
includes the red commits in exactly the same places as they are
already on the remote branch. When the remote repository replaces its
master with the one we are pushing, it loses nothing, because the
red commits are identical. All it needs to do is to add the
blue commits onto the end and then move its master ref forward to
point to the last blue commit instead of to the last red commit. This
is a “fast-forward”.
At this point, the push is successful, and the git-push command also
updates the tracking branch to reflect that the remote branch
has moved forward. I did not show this in the illustration.
But wait, what if someone else had added yet more commits to the
remote master while we were executing steps 4 and 5? Wouldn't our
new push attempt fail just like the first one did? Yes, absolutely!
We would have to repeat steps 4 and 5 and try a third time. It is
possible, in principle, to be completely prevented from pushing
commits to a remote repo because it is always changing so quickly that
you never get caught up on its current state. Repeated push failures
of this type are sign that the project is large enough that
repository's owner needs to set up a more structured code release
mechanism than “everyone lands stuff on master whenever they feel
like it”.
An earlier draft of this article ended at this point with “That is all
I have to say about this.” Ha!
Unavoidable problems
Everyone suffers through this issue at some point or another. It is
tempting to wonder if Git couldn't somehow make it easier for people
to deal with. I think the answer is no. Git has multiple,
distributed repositories. To abandon that feature would be to go back
to the dark ages of galley slaves, smallpox, and SVN. But if you have
multiple distributed anythings, you must face the issue of how to
synchronize them. This is intrinsic to distributed systems: two
components receive different updates at the same time, and how do you
reconcile them?
For reasons I have discussed before, it
does not appear possible to automate the reconciliation in every case
in a source code control system, because sometimes the reconciliation
may require going over to a co-worker's desk and arguing for two
hours, then calling in three managers and the CTO and making a
strategic decision which then has to be approved by a representative
of the legal department. The VCS is not going to do this for you.
I'm going to digress a bit and then come back to the main point.
Twenty-five years ago I taught an introductory programming class in C.
The previous curriculum had tried hard to defer pointers to the middle
of the semester, as K&R does (chapter 7, I think). I decided this was
a mistake. Pointers are everywhere in C and without them you can't
call scanf or pass an array to a function (or access the command-line
arguments or operate on strings or use most of the standard library
or return anything that isn't a
number…). Looking back a few years later I wrote:
Pointers are an essential part of [C's] solution to the data hiding
problem, which is an essential issue. Therefore, they cannot be
avoided, and in fact should be addressed as soon as possible. …
They presented themselves in the earliest parts of the material not
out of perversity, but because they were central to the topic.
I developed a new curriculum that began treating pointers early on,
as early as possible, and which then came back to them repeatedly, each time
elaborating on the idea. This was a big success. I am certain that
it is the right way to do it.
(And I've been intending since 2006 to write an article about K&R's
crappy discussion of pointers and how its deficiencies and omissions
have been replicated down the years by generation after generation of
C programmers.)
I think there's an important pedagogical principle here. A good
teacher makes the subject as simple as possible, but no simpler. Many
difficult issues, perhaps most, can be ignored, postponed, hidden,
prevaricated, fudged,
glossed over, or even solved. But some must be met head-on and dealt
with, and for these I think the sooner they are met and dealt with, the better.
Push conflicts in Git, like pointers in C, are not minor or
peripheral; they are an intrinsic and central issue. Almost everyone is
going to run into push conflicts, not eventually, but right away.
They are going to be completely stuck until they have dealt with
it, so they had better be prepared to deal with it right away.
If I were to write a book about Git, this discussion would be in
chapter 2. Dealing with merge conflicts would be in chapter 3. All the
other stuff could wait.
That is all I have to say about this. Thank you for your kind
attention, and thanks to Sumana Harihareswara and AJ Jordan for
inspiration.
[Other articles in category /prog]
permanent link
Git remote branches and Git's missing terminology
Beginning and even intermediate Git users have several common problem
areas, and one of these is the relationship between remote and local
branches. I think the basic confusion is that it seems like there
ought to be two things, the remote branch and the local one, and you
copy back and forth between them. But there are not two but three,
and the Git documentation does not clearly point this out or adopt
clear terminology to distinguish between the three.
Let's suppose we have a remote repository, which could be called
anything, but is typically named origin . And we have a local
repository which has no name; it's just the local repo.
And let's suppose we're working on a branch named master , as one
often does.
There are not two but three branches of interest, and they might all
be pointing to different commits:
The branch named master in the local repo. This is where we do
our work and make our commits. This is the local branch. It is
at the lower left in the diagram.
The branch named master in the remote repo. This is the remote
branch, at the top of the diagram. We cannot normally see this at
all because it is (typically) on another computer and (typically)
requires a network operation to interact with it. So instead, we
mainly deal with…
The branch named origin/master in the local repo. This is
the tracking branch, at the lower right in the diagram.
We never
modify the tracking branch ourselves. It is automatically
maintained for us by Git. Whenever Git communicates with the
remote repo and learns something about the disposition of the
remote master branch, it updates the local branch
origin/master to reflect what it has learned.
I think this triangle diagram is the first thing one ought to see when
starting to deal with remote repositories and with git-fetch and
git-push .
The Git documentation often calls the tracking branch the
“remote-tracking branch”. It is important to understand that the
remote-tracking branch is a local branch in the local repository.
It is called the “remote-tracking” branch because it tracks the state
of the remote branch, not because it is itself remote. From now on I
will just call it the “tracking branch”.
Now let's consider a typical workflow:
We use git fetch origin master . This copies the remote branch
master from the remote repo to the tracking branch
origin/master in the local repo. This is the green arrow in the
diagram.
If other people have added commits to the remote master branch
since our last fetch, now is when we find out what they are. We
can compare the local branch master with the tracking branch
origin/master to see what is new. We might use git log
origin/master to see the new commits, or git diff origin/master
to compare the new versions of the files with the ones we had
before. These commands do not look at the remote branch! They
look at the copy of the remote branch that Git retrieved for us.
If a long time elapses between the fetch and the compare, the
actual remote branch might be in a completely different place than
when we fetched at it.
(Maybe you use pull instead of fetch . But pull is exactly
like fetch except that it does merge or rebase after the fetch completes.
So the process is the same; it merely combines this step and the
next step into one command. )
We decide how to combine our local master with origin/master . We
might use git merge origin/master to merge the two branches, or
we might use git rebase origin/master to copy our new local
commits onto the commits we just fetched. Or we could use git
reset --hard origin/master to throw away our local commits (if
any) and just take the ones on the tracking branch. There are a
lot of things that could happen here, but the blue arrow in the
diagram shows the general idea: we see new stuff in origin/master
and update the local master to include that
new stuff in some way.
After doing some more work on the local master , we want to
publish the new work. We use git push origin master . This is
the red
arrow in the diagram. It copies the local master to the remote
master , updating the remote master in the process. If it is
successful, it also updates the tracking branch
origin/master to reflect the new position of the remote master .
In the last step, why is there no slash in git push origin master ?
Because origin/master is the name of the tracking branch, and
the tracking branch is not involved. The push command gets
two arguments: the name of the remote (origin ) and the branch to
push (master ) and then it copies the local branch to the remote one
of the same name.
Deleting a branch
How do we delete branches? For the local branch, it's easy: git
branch -d master does it instantly.
For the tracking branch, we include the -r flag: git branch
-d -r origin/master . This deletes the tracking branch, and
has no effect whatever on the remote repo. This is a very unusual
thing to do.
To delete the remote branch, we have to use git-push because that
is the only way to affect the remote repo. We use git push origin
:master . As is usual with a push, if this is successful Git also
deletes the tracking branch origin/master .
This section has glossed over an important point: git branch -d
master does not delete the master branch, It only deletes the
ref, which is the name for the branch. The branch itself remains.
If there are other refs that refer to it, it will remain as long as
they do. If there are no other refs that point to it, it will be
deleted in due course, but not immediately. Until the branch is
actually deleted, its contents can be recovered.
Hackery
Another way to delete a local ref (whether tracking or not) is just to
go into the repository and remove it. The repository is usually in a
subdirectory .git of your working tree, and if you cd .git/refs
you can see where Git records the branch names and what they refer to.
The master branch is nothing more nor less than a file heads/master
in this directory, and its contents are the commit ID of the commit to
which it refers. If you edit this commit ID, you have pointed the
ref at a different commit. If you remove the file, the ref is
gone. It is that simple.
Tracking branches are similar. The origin/master ref is
in .git/refs/remotes/origin/master .
The remote master branch, of course, is not in your repository at
all; it's in the remote repository.
Poking around in Git's repository is fun and rewarding. (If it
worries you, make another clone of the repo, poke around in the clone,
and throw it away when you are finished poking.) Tinkering with the
refs is a good place to start Git repo hacking: create a couple of
branches, move them around, examine them, delete them again, all
without using git-branch . Git won't know the difference. Bonus fun
activity: HEAD is defined by the file .git/HEAD . When you make a
new commit, HEAD moves forward. How does that
work?
There is a
gitrepository-layout manual
that says what else you can find in the repository.
Failed pushes
We're now in a good position to understand one of the most common
problems that Git beginners face: they have committed some work, and
they want to push it to the remote repository, but Git says
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'remote'
something something fast-forward, whatever that is
My article explaining this will
appear here on Monday. (No, I really mean it.)
Terminology problems
I think one of the reasons this part of Git is so poorly understood is
that there's a lack of good terminology in this area. There needs to
be a way to say "the local branch named master ” and “the branch
named master in the remote named origin ” without writing a five-
or nine-word phrase every time. The name origin/master looks like
it might be the second of these, but it isn't. The documentation uses
the descriptive but somewhat confusing term “remote-tracking branch”
to refer to it. I think abbreviating this to “tracking branch” would
tend to clear things up more than otherwise.
I haven't though of a good solution to the rest of it yet. It's
tempting to suggest that we should abbreviate “the branch named
master in the remote named origin ” to something like
“origin :master ” but I think that would be a disaster. It would be
too easy to confuse with origin/master and also with the use of the
colon in the refspec arguments to git-push . Maybe something like
origin -> master that can't possibly be mistaken for part of a shell
command and that looks different enough from origin/master to make
clear that it's related but not the same thing.
Git piles yet another confusion on this:
$ git checkout master
Branch master set up to track remote branch master from origin.
This sounds like it has something to with the remote-tracking branch,
but it does not! It means that the local branch master has been
associated with the remote origin so that fetches and pushes that
pertain to it will default to using that remote.
I will think this over and try to come up with something that sucks a
little less. Suggestions are welcome.
[Other articles in category /prog]
permanent link
Base-4 fractions in Telugu
Rik Signes brought to my attention that since version 5.1 Unicode has
contained the following excitingly-named characters:
0C78 ౸ TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR
0C79 ౹ TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR
0C7A ౺ TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR
0C7B ౻ TELUGU FRACTION DIGIT THREE FOR ODD POWERS OF FOUR
0C7C ౼ TELUGU FRACTION DIGIT ONE FOR EVEN POWERS OF FOUR
0C7D ౽ TELUGU FRACTION DIGIT TWO FOR EVEN POWERS OF FOUR
0C7E ౾ TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
I looked into this a little and found out what they are for. It makes
a lot of sense! The details were provided by
“Telugu Measures and Arithmetic Marks” by Nāgārjuna Venna.
Telugu is the third-most widely spoken language in India, spoken
mostly in the southeast part of the country. Traditional Telugu units
of measurement are often divided into four or eight subunits. For
example, the tūmu is divided into four kuṁcamulu, the kuṁcamulu,
into four mānikalu, and the mānikalu into four sōlalu.
These days they mainly use liters like everyone else. But the
traditional measurements are mostly divided into fours, so amounts are
written with a base-10 integer part and a base-4 fractional part. The
characters above are the base-4 fractional digits.
To make the point clearer, I hope, let's imagine that we are using the
Telugu system, but with the familar western-style symbols 0123456789
instead of the Telugu digits
౦౧౨౩౪౫౬౭౮౯.
(The Telugu had theirs first
of course.) And let's use 0-=Z as our base-four fractional digits,
analogous to Telugu ౦౼౽౾. (As in Telugu, we'll use the same zero
symbol for both the integer and the fractional parts.) Then to write
the number of gallons (7.4805195) in a cubic foot, we say
7.-Z=Z0
which is 7 gallons plus one (-) quart plus three (Z) cups plus two
(=) quarter-cups plus three (Z) tablespoons plus zero (0) drams,
a total of 7660 drams almost exactly. Or we could just round off to
7.=, seven and a half gallons.
(For the benefit of readers who might be a bit rusty on the details of
these traditional European measurements, I should mention that there
are four drams in a tablespoon, four tablespoons in a quarter cup, four
quarter cups in a cup, four cups in a quart, and four quarts in a
gallon, so 4⁵ = 1024 drams in a gallon and 7.4805195·4⁵ = 7660.052
drams in a cubic foot. Note also that these are volume (fluid) drams,
not mass drams, which are different.)
We can omit the decimal point (as the Telegu did) and write
7-Z=Z0
and it is still clear where the integer part leaves off and the
fraction begins, because we are using special symbols for the
fractional part. But no, this isn't quite enough, because if we wrote
20ZZ= it might not be clear whether we meant 20.ZZ= or 2.0ZZ=.
So the system has an elaboration. In the odd positions, we don't
use the 0-=Z symbols; we use Q|HN instead. And we don't write 7-Z=Z0,
we write
7|ZHZQ
This is always unambiguous: 20.ZZ= is actually written 20NZH
and 2.0ZZ= is written 2QZN=, quite different.
This is all fanciful in English, but Telugu actually did this.
Instead of 0-=Z they had ౦౼౽౾ as I mentioned before. And instead
of Q|HN they had ౸౹౺౻. So if the Telugu were trying to write
7.4805195, where we had 7|ZHZQ they might have written
౭౹౾౺౾౸. Like us, they then appended an abbreviation for the unit of
measurement. Instead of “gal.” for gallon they might have put ఘ
(letter “gha”), so ౭౹౾౺౾౸ఘ. It's all reasonably straightforward, and
also quite sensible. If you have ౭౹౾౺ tūmu, you can read off
instantly that there are ౺ (two) sōlalu left over, just as you can
see that $7.43 has three pennies left over.
Notice that both sets of Telugu fraction digits are easy to remember:
the digits for 3 have either three horizonal strokes ౾ or three
vertical strokes ౻, and the others similarly.
I have an idea that the alternating vertical-horizontal system might
have served as an error-detection mechanism: if a digit is omitted,
you notice right away because the next symbol is wrong.
I find this delightful. A few years back I read all of The Number
Concept: Its Origin and
Development (1931) by Levi
Leonard Conant, hoping to learn something really weird, and I was
somewhat disappointed. Conant spends most of his book describing the
number words and number systems used by dozens of cultures and almost
all of them are based on ten, and a few on five or twenty. (“Any
number system which passes the limit 10 is reasonably sure to have
either a quinary, a decimal, or a vigesimal structure.”) But he does
not mention Telugu!
[ Addendum 20200821: Bengali currency amounts do something
similar, but the sections alternate
between base-10 and base-4 numerals! ]
[Other articles in category /math]
permanent link
Annual self-evaluation time, woo-hoo!
It's annual performance evaluation time at my employer, ZipRecruiter, and as
part of that I have to write a self-evaluation. I know many people
dread these, and I used to dread them, but these days I like doing it.
Instead of being a torture or even a chore, for the last couple of
years I have come out of it feeling much better about my job and my
performance than I went in.
I think that is about 20% because my company does it in a good way,
30% because it suits my personality, and 50% because I have learned
how to handle it well. The first half of that might not help you
much, but if you're an evaluation loather, you might be able to
transfer some of the second half and make it a little less horrible
for yourself.
How ZipRecruiter does self-evaluations
I will get this out of the way because it's quick. ZipRecruiter does these
evaluations in a way that works well for me. They do not pester me
with a million questions. They ask only four, which are roughly:
- What were your main accomplishments this year?
- Describe areas you feel require improvement.
- What do you hope to accomplish in the coming year?
- How can your manager help you?
I very much appreciate this minimalist approach. It gets right to the
point, covers all the important issues and nothing more. None of
these questions feels to me like a meaningless bureaucratism or a
waste of time.
Answering the questions thoroughly takes (only) two or three hours,
but would take less if I didn't write such detailed answers; I'm sure
I could write an acceptable report in an hour. I can see going in
that it will be a finite process.
Why this suits my personality well
If you have followed this blog for a while, you may have noticed that
I like writing essays, particularly essays about things I have been
working on or thinking about. ZipRecruiter's self-evaluation process invites me
to write a blog post about my year's work. This is not everyone's cup
of tea, but it is right up my alley. Tea alley. Hee hee.
My brain has problems
My big problems with writing a self-evaluation are first, that I have
a very poor memory, and second, that I think of myself as a lazy
slacker who spends a lot of time goofing off and who accomplishes very
little. These combine badly at evaluation time.
In the past, I would try to remember what work I did in the previous
year so I could write it up. My memory is poor, so I wouldn't
remember most of what I had done, and then it was easy to come to the
conclusion that I had not done very much, probably because I was a
lazy slacker whose spent a lot of time goofing off. I would go
through several iterations of this, until, tormented by guilt and
self-hatred, I would write that into the self-evaluation. This is not
a practice I would recommend.
If there were two projects, A and B, and I promptly finished A but B
dragged on and was running late, which one would I be more likely to
remember when the time came to write the self-evaluation report? B,
of course. It was still on my mind because I spent so long thinking
about it and because it was still in progress. But I had forgotten
about A immediately after putting it to rest. Since I could remember
only the unfinished projects, I would conclude that I was a lazy
slacker who never finished anything, and write that into the
self-evaluation. This is also a a practice I recommend you avoid.
The ticketing system is my bionic brain
The way I have been able to escape this horrible trap is by
tracking every piece of work I do, every piece, as a ticket in our
ticketing system. People often come to me and ask me to do
stuff for them, and I either write up a ticket or I say “sure, write
me a ticket”. If they ask why I insist on the ticket (they usually
don't), I say it's because when self-evaluation time comes around I
want to be able to take credit for working on their problem. Everyone
seems to find this reasonable.
Then, when it's time to write the self-evaluation, the first thing I
do is visit the ticket website, select all my tickets from the past
year, and sort them into chronological order. I look over the list of
ticket titles and make a list of stuff that might be worth mentioning
on the evaluation. I will have forgotten about three-fourths of it.
If I didn't have the list in the ticketing system, I would only
remember the most recent one-fourth and conclude that I spent
three-fourths of my time goofing off because I am a lazy slacker.
Instead, there is this long list of the year's accomplishments, too
many to actually include in the report.
Well, this is not rocket science. One is called upon to describe the
year's accomplishments. Even people with better memory than me surely
cannot remember all this without keeping records, can they? Anyway I
surely cannot, so I must keep records and then consult them when the
time comes. Put that way, it seems obvious. Why did it take so long
to figure out? But there are a lot of happy little details that were
not so obvious.
Instead of thinking “Why didn't I finish big project X? I must have
been goofing off. What a lazy slacker I am” I think “holy cow, I
resolved 67 tickets related to big project X! That is great
progress! No wonder I got hardly anything else done last fall” and
also “holy cow, X has 78 resolved tickets and 23 still open. It is
huge! No wonder it is not finished yet.”
Writing “I completed 67 tickets related to X” is a lot more concrete
than “I worked hard on X”. If you are neurotic in the way I am, and
concerned that you might be a lazy slacker, it feels much more
persuasive. I have an idea that it sounds better to my boss also,
particularly if he were to be called upon to discuss it with his
manager. (“Under my leadership, Mark completed 67 tickets related
to X!”) Andy Lester says that your job is to make your boss happy,
and that means making it easy for him to do his job, which is to
make his boss happy. So this is no small thing.
Instead of thinking “Gee, the CTO declared top priority initiative
Y, and while everyone else was working hard on it I mostly ignored
it because I am a lazy slacker” I might see that I have tagged 9
tickets “top priority initiative Y”. Then on the report, I proudly
boast “I completed 9 tickets in support of the CTO's mandate,
including (most important one) and (most impressive one).” This
also comes under the heading of “make it easy for your boss to do
his job”.
Instead of completely forgetting that I did project Z, I see the
tickets and can put it in my report.
Instead of remembering awful project W, which dragged on for months,
and thinking what a lazy slacker I was because I couldn't get it
done, I have a progress record in the ticket and the details might
suggest a different interpretation: Project W sucked, but I
nevertheless pursued it doggedly to completion, even though it took
months.
I might remember that I once helped Jones, but what did I help him
with? Did I really spend much time on him? Without looking at the
ticket list, I might not realize that I helped Jones every few weeks
all year long. This sort of pattern is often apparent only in the
retrospective summary. With the ticket system, instead of “oh,
Jones sometimes asks me questions, I guess” I can see that
supporting Jones was an ongoing thing and he kept coming back. This
goes into the report: “I provided ongoing support to Jones,
including (some cherry-picked example that makes me look especially
good).”
One question (#2) on the report form is “Describe areas you feel
require improvement”. If I wrote in last year's report that I would
like to improve at doing X, I can look in the ticket system for
specific evidence that I might have improved, even if I wasn't
consciously trying to improve X at the time. Probably there is
something somewhere that can at least be spun as an attempt to
improve at X. And if it didn't actually improve X, I can still ask
myself why it didn't and what might work instead, and put that in
the report as something to try next time, which is question #3.
Hey, look at that, I am evaluating my prior performance and making
informed corrections. That might be a useful practice. Wouldn't it
be great if I took time every so often to do that? Some sort of
periodic self-evaluation perhaps?
Another question (#3) is “What would you like to do in the coming
year?” If I wrote in last year's report said “I would like to do
more of X” I can look for evidence that I did do that, and then
write it into this year's report: “Last year I said I would try to
do more of X, and I did.”
Even if I were having a bad year and got very little done—and this
has happened—having a list of the stuff I did get
done leaves me in a much better position to write the report than
not having such a list.
None of this good stuff would be possible without an actual record of
what I did. If there weren't a ticketing system, I would have to
invent one or maybe tattoo it on my chest like the guy in Memento.
Even aside from its use in writing annual self-evaluations, keeping a
work diary is crucial for me, because without it I can't remember
day-to-day what I am working on and what needs to happen next. And
even for people with better memory than me, are they really going to
remember all 317 things they did for work this year, or that 67 of
them pertained to big project X? If they can that's great but I doubt
it.
Keeping a work record is part of my job
I think it is important to maintain the correct attitude to this. It
would be easy to imagine ticket management as unproductive time
that I wasted instead of accomplishing something useful. This
is wrong. The correct attitude is to consider ticket updates to be part of my work product: I
produce code. I produce bug fixes. I produce documentation, reports,
and support interactions. And I also produce ticket updates. This is
part of my job and while I am doing it I am not goofing off, I am not
procrastinating, I am doing my job and earning my salary. If I spent
the whole day doing nothing but updating tickets, that would be a day
well-spent.
Compare “I produce ticket updates” with “I produce unit tests”. The
attitude for ticket updates is the same as for testing. When
something happens in a project, I update the ticket, because keeping
the tickets updated is part of the project, just like writing tests
is.
An organization that fails to support ticket updates is broken in the
same way as one that fails to support test development.
My boss gets email every time I update a ticket. I don't know if he
reads these, but he has the option to, and I don't need to worry as
much that maybe he thinks I am a lazy slacker who is goofing off,
because he is getting a stream of automatic notifications about what I
am up to. I'm not my boss but if I were I would appreciate this very
much.
Maybe some of this can help you?
There might be some use in this even for people who aren't already in
the habit of writing self-absorbed blog posts.
If doing the annual self-evaluation makes you suffer, maybe it would
help to practice writing some blog posts. You don't have to publish
them or show anyone. Next time you finish a project, set aside thirty
or sixty minutes to try to write up a little project report: What
worked, what didn't, what are you pleased about, what was surprising,
what was fun, what was annoying? I'm not certain this will help but
it seems like this is a skill that might get easier with practice, and
then when you have to write your annual self-evaluation it might be
easier because you have more practice doing it. Also, you would have
a little file of material to draw on and would not have to start from
zero.
If your employer's process requires you to fill in some giant
questionnaire, it might be easier to do if you go into it with answers
to the four basic questions prepared ahead of time. (I imagine that
it's even possible that if you address the four big questions and
ignore everything on the giant questionnaire that doesn't pertain to
them, everyone will be perfectly satisfied and nobody will notice the
omissions.)
And keep a work diary! Tattoo it on your chest if you have to. If it
seems daunting, realize that you don't have to do it all at once.
Keeping a work diary of some sort is forgiving in the same way as
writing unit tests:
It's not all-or-nothing, you don't have to test every piece of
code to get any benefit from testing. If you write tests for 1%
of the code, you get about 1% of the benefit, and you can ramp up.
If you break your test-writing streak you don't have to start over
from zero. If you didn't write any tests for the code you wrote
last week, that's a shame, but it doesn't affect the benefit you
can get from writing a unit test for whatever you're working on
today.
The work diary is similar. When time comes to write your evaluation,
a small and incomplete record is better than no record at all. If you
forget to write in the diary for a month, that's a shame, but it
doesn't affect the benefit you can get from writing down today what
you did today.
Our ticketing system
This isn't important, but I expect someone will want to know: At work
we use FogBugz. Some of my co-workers dislike it but I have no major
complaints. If you want to try it on your own, they have a seven-day
free trial offer, after which
you can sign up for a permanent free account that supports up to two
users. I am experimenting with using a free tier account to manage my
personal to-do list.
Coming soon
I wrote another 2,000 words about my specific practices for managing
tickets. I hope it'll be along in a few days.
[ Addendum 20190701: Julia Evans has written an article on the same
topic, but with more details,
useful suggestions I hadn't thought of, and also without all the
neurosis.
I don't know why I haven't published my promised article on ticket
management. (“Coming soon”, ha ha.) It looks pretty good. Later
this week, maybe? ]
[Other articles in category /misc]
permanent link
|