Wednesday, March 12, 2025

It's performance review time

`Tis the season for performance reviews. That time of year when we give, and get, evaluations of our annual performance. These annual rituals come with occasional joy and often a lot of shock.

In Radical Openness, there is a lesson on Learning from Corrective Feedback, which seems applicable to all those suffering through this season.

Criticism or feedback from another person is not truth but a belief held by the other person that may be true, partially true, or not true at all.

Ask the following twelve questions to help determine whether to accept or decline the feedback:

  1. Does the person have more experience than I do in this area? YES/NO
  2. Will accepting the feedback help maintain my relationship with the person giving me feedback? YES/NO
  3. Will accepting the advice help me maintain or improve other important relationships? YES/NO
  4. Am I discounting the feedback on purpose to displease or punish the person? YES/NO
  5. If necessary, am I capable of making the changes that are being suggested? YES/NO
  6. Will accepting the feedback help me steer clear of significant problems (for example, financial loss, employment difficulties, problems with the law)? YES/NO
  7. Was the person providing the feedback using a calm and easy manner? YES/NO
  8. Does the feedback refer to the actual situation I am in, as opposed to the past or future? YES/NO
  9. Am I in a long term caring relationship with this person? YES/NO
  10. Is the feedback I am being given something that I have heard from others before? YES/NO
  11. Am I tense or frustrated about this feedback? YES/NO
  12. Am I saying to myself, I know I am right, no matter what the other person says or how things seem? YES/NO

Total up the number of YES responses ... Then use the following key to guide [you] in deciding whether to accept or decline the feedback.

  • 11 to 12 YES responses = accept the feedback as accurate and effective, no matter what
  • 9 to 10 YES responses = accept the feedback as likely accurate and effective
  • 7 to 8 YES responses = accept the feedback as possibly accurate and effective; continue to evaluate whether it is useful or true
  • 5 to 6 YES responses = accept the feedback, but very tentatively
  • 3 to 4 YES responses = tentatively decline the feedback, but with an open mind
  • 1 to 2 YES responses = decline the feedback

Wednesday, August 10, 2022

On the Fight for Software Liberty

RMS writes in Why Open Source Misses the Point of Free Software:

Sometimes [developers of proprietary software] produce a program that is powerful and reliable, even though it does not respect the users' freedom. Free software activists and open source enthusiasts will react very differently to that.

A pure open source enthusiast, one that is not at all influenced by the ideals of free software, will say, “I am surprised you were able to make the program work so well without using [the Open Source] development model, but you did. How can I get a copy?” This attitude will reward schemes that take away our freedom, leading to its loss.

The free software activist will say, “Your program is very attractive, but I value my freedom more. So I reject your program. I will get my work done some other way, and support a project to develop a free replacement.” If we value our freedom, we can act to maintain and defend it.

The last sentence is crucial, and yet RMS fails to convey the import: "we can act", sure, but to "maintain and defend" freedom one must fight for it.

In a 2002 interview, RMS states:

[W]e need to think about right and wrong in making our decisions...

Every decision, every time, we must choose the path that defends freedom and liberty.

Proprietary software aims to maximize control. Open source software aims to maximize distribution. Neither aims to maintain liberty, because neither made their first decisions around user freedom: the right to determine their use of software, not to be dictated by the software's regime.

Every right conveys a freedom, and every right requires a responsibility. Your responsibility, to yourself, your family, and your community, is to fight for your freedom with every choice.

This is all easy to say and difficult to do, once accustomed to convenience or the illusion of control. For example, I write this now on a MacOS using Safari. Google Fiber provides my Internet. My car is computer controlled, and I have no idea how that software operates. My phone is Android, Google again. I choose these because they're functional for my needs, I choose to work within the features they offer, and I can afford them. But I am beholden to them.

In Who does that server really serve?, RMS writes:

[P]ower is something we must resist.

From awareness comes suspicion and from suspicion comes resistence.

Everyone will have their own journey along the path, but I encourage readers to start by raising their own awareness to the software around them that holds power over them. Then to begin by questioning whether that power is something you want to give away. If not, consider how you begin your own resistance and fight toward a future where all software is under your control.

Tuesday, July 14, 2020

rm != rg

I use ripgrep, rg. It's like recursive grep, only 100x better.

Today, I had this session:

$ rm pattern
rm: pattern: No such file or directory
$ rg pattern
$

See that? I meant to type "rg pattern" the first time, but typed "rm pattern". Luckily no harm was done in this case. But what if I had typed "rm tod*", wanting to search for the string "to" followed by zero or more "d"? Well, I might just blow away my todo file, since it matches the glob tod*. And then I'd be sad.

I'm not sure if the ergomomics of rg as a command name have come up before, but this scary close encounter has me thinking. Maybe I should do this sly manuever, as suggested by a colleague: alias ag=rg.

I don't know. I think I'm tempted more so to make rm with its dangerous side effects harder to invoke. Like so:

alias remove=rm
rm() {
  echo "If you want to destroy a file, use remove."
  echo "If you want to find a file, use rg."
}

What do you think?

Tuesday, October 30, 2018

Friday, September 8, 2017

Choosing the first available program from list of options

GNU tar accepts an external program to perform compression, via the option --use-compress-program. I'd normally want pigz if it's available, but if not, fallback to gzip. Is there a compact way to get represent this? Yes!
which --skip-alias --skip-functions pigz gzip 2>/dev/null | head -1
GNU which accepts multiple arguments, printing out the resolution for each as they're found or an error if not. GNU which also allows finding only full-fledged binaries, not aliases or functions. This is exactly what we want: list the paths to these programs, in the order I gave, then pluck the first one.

Monday, March 27, 2017

What is a @dataProvider?

I'm asked about data providers almost every time I introduce a developer to PHPUnit. Once you understand them, they're quite clear, but on first pass they seem to give developers pause.

So what are they? Practically, a data provider is any static method that produces an array of arrays. The outer array defines the iterations of the test loop, while the inner arrays are the arguments to pass to each iteration. Let's look at an example. First, annotate a test method's docblock:

/**
 * @dataProvider provides_foo_and_bar
 */
public function test_frobnicator($foo, $bar) { /* ... */ }

Then define the data provider:

public static function provides_foo_and_bar() {
    return [
        [ 'FOO', 'BAR' ],
        [ 'BAZ', 'QUUX' ],
    ];
}

PHPUnit will call provides_foo_and_bar twice. The first time it will pass test_frobnicator with "FOO" and "BAR". The second time it will call test_frobnicator with "BAZ" and "QUUX". Note that the data provider is both public and static: PHPUnit requires that.

Pro-top: by default, phpunit runs all data sets. But, you can select specific data sets to run easily: phpunit FrobnicateModel.php test_frobnicate#1 runs only one loop, with the 1-index elements "BAZ" and "QUUX".

Thursday, December 15, 2016

Approximating GNU parted in Windows

I partition disks in Linux all the time. But, thanks to disk ghosting, I don't do much partitioning in Windows. When I do need to partition in Windows (like external drives), what do I use? Enter the Windows Disk Management Snap-in, diskmgmt.msc, first available in Windows 98.
If you're familiar with GNU parted, this Windows tool will make perfect sense. You see immediately your list of hard drives, their partitions, and can click on them to delete or resize. Click on free space to partition. There are some limitations, though. For example, you can't delete recovery partitions. For that, you can drop to the Windows command line and run diskpart. This tool is like Linux's fdisk.

Thursday, December 8, 2016

Identifying specific vulnerabilities in WordPress, by version

Exactly how vulnerable is your WordPress version? Ask the good folks over at the WordPress vulnerability database who have not only assembled a vulnerability list by version, but also provided a nice API for querying.

# WordPress 4.4.2 vulnerabilities, by type
$ curl -sS https://wpvulndb.com/api/v2/wordpresses/442 |\
  jq -r '.["4.4.2"]|.["vulnerabilities"]|.[].vuln_type' |\
  sort | uniq -c
      1 BYPASS
      1 CSRF
      1 LFI
      1 SSRF
      1 UNKNOWN
      5 XSS
Same thing, but list the titles and take a version as a parameter:
wpvulndb() {
    version=${1:?Check which WordPress version for vulnerabilities (eg 4.8.3)?}
    curl -sS "https://wpvulndb.com/api/v2/wordpresses/${version//./}" | \
      jq -r --arg version "$version" '.[$version]|.["vulnerabilities"]|.[].title'
}

wpvulndb 4.8.3

Wednesday, November 30, 2016

Pasting a remote file into your local clipboard (* mouse not required)

So, I'm updating a configuration file on a remote server (using MobaXterm), and I need to copy the contents into some Trello documentation running in a browser on my local Windows machine.

The old fashioned way to do it is to select it with the mouse (which MobaXterm interprets as copying to my Windows clipboard), then Shift+Insert it into the browser. Well, turns out you can use the command line:

[Bishop@Cygwin]$ ssh user@host "< /path/to/file" | clip

On Windows, clip is a program to read from standard in and put into the Windows clipboard. On Mac OSX, replace clip with pbcopy for the same effect.

You could extend this approach: instead returning the whole file, return the result of a pipe line. Neat. Like magic, no more mouse needed.

Friday, November 11, 2016

Bypassing private and protected visibility in PHP

Members declared protected can be accessed only within the class itself and by inherited classes. Members declared as private may only be accessed by the class that defines the member.

This is true only in an academic sense: code outside the object can still get and set private and protected members. As usual in PHP, all it takes is a little magic.

Wednesday, March 23, 2016

The end of the mouse era

Doug Englebart invented the computer mouse nearly 50 years ago. Before HD, before GPU, before UX, the mouse let people interact with an information rich virtual space with ease.

The generation whose work productivity preceded the mouse are retiring. Today's work force learned young or grew up with computer mice. We are comfortable with them. But the plunging cost of touch screen, the integration of draw-capable technologies in underlying OS, and the rise of hand-held form factor computing all spell the end of the mouse age.

Our generation may find it difficult to imagine a world without mice. But consider, if price were not an object, would you rather have a mouse or a touch screen?

Fundamentally, a mouse is the wrong tool for the job. If you want to select, move, shrink or otherwise manipulate windows, keyboard chords provide the necessary precision and do not change your locus of attention. If you want to scroll, page and cursor keys provide two resolutions of movement. If you want to draw a freehand shape, a touch screen or a digitizing tablet offers measurably better precision.

In the future, we'll see a world without mice. A world with keyboards and touch screens. When economic factors allow cheap, ubiquitous touch input, mice commodity will become a novelty. Good riddance I say.


Addendum
I was just asked how I navigate web pages without a mouse. The answer: vimium. Since I use vi, this is a natural move more me. Props to mjmccull for introducing this extension to me years ago. Read up on vimium in this quick guide.

Bonus
Did you know that Windows+B+Enter opens the Windows system tray? Here's a running list of Windows 10 keyboard shortcuts to help you cut your mouse cord.
Shortcut Key CombinationAction or Effect
Windows+B+EnterRaise the Windows system tray. Use your cursor keys to navigate the tray icons
Windows+Shift+RightMove the active window right. Try also with the left cursor key.

Thursday, March 3, 2016

Evoking all possible test failure modes in PHPUnit

When you're writing your own PHPUnit test listener, you need a test case that evokes all the different PHPUnit test states. Here's you go:
<?php
class EvokesTest extends \PHPUnit_Framework_TestCase
{
    public function test_pass()
    {
    }

    public function test_fail()
    {
        $this->fail(__FUNCTION__);
    }

    public function test_error()
    {
        throw new \RuntimeException(__FUNCTION__);
    }

    public function test_skipped()
    {
        $this->markTestSkipped(__FUNCTION__);
    }

    public function test_incomplete()
    {
        $this->markTestIncomplete(__FUNCTION__);
    }

    public function test_risky()
    {
        throw new \PHPUnit_Framework_RiskyTestError;
    }
}

Thursday, February 25, 2016

Disabling Plugins in Jenkins

If a plugin upgrade causes problems, Jenkins may not restart. You'll be welcomed by an error message and a stack trace. Don't panic! Go into your Jenkins plugin directory, list files by date, and then disable the most recent ones:
$ cd $JENKINS_HOME/plugins
$ ls -ltr *.jpi
-rw-r--r--. 1 root root   169194 Feb 22 10:12 script-security.jpi
-rw-r--r--. 1 root root   516115 Feb 22 10:12 next-executions.jpi
-rw-r--r--. 1 root root   739004 Feb 22 10:12 email-ext.jpi
$ touch email-ext.jpi.disabled next-executions.jpi.disabled script-security.jpi.disabled
$ service jenkins restart
Files ending in .disabled instruct Jenkins to disable the corresponding plugin. Delete the disabling files until you've found the offending plugin. Then you can go into Jenkins and revert it to an earlier version. Word of advice: Upgrade plugins in small batches. Doing so helps you isolate early problematic plugins.

Thursday, February 18, 2016

Big list of files to edit? vim to the rescue (again)

Did you know that you can treat the text under the cursor as a filename, and open that up for editing right in vim? Here's how:

  • gf will open the filename under the cursor in the current window
  • ^Wf will open it in a split window
  • ^Wgf will open it in a new window

Thursday, February 11, 2016

Thursday, February 4, 2016

Private methods are collaborators in disguise

Private methods cannot be unit tested, only integration tested through whatever public methods call them. I find this unsettling. I want to unit test private methods, so that I know the public methods are composed of independently verified code. What to do?

My first tactic is to avoid private methods. When behaviors are small enough, the need for private methods diminishes.

My second tactic is to promote them to public, but document that they aren't part of the API. This feels like a hack. A trick that's necessary because I've not thought about the design deep enough. I do this more than I like, honestly, because it's so quick and cheap to do.

Today, I thought of another approach that reinforces the first tactic. Maybe the apparent need for private methods is a signal that what I really want is a collaborator. Instead of privately doing a bit of work in furtherance of a class behavior goal, delegate that work to a first-class worker. Example? Sure!

Suppose I'm writing a class to model a web request. Part of web requests are MIME content type headers. These headers have specific formats for which a parser is needed. I could build parsing into my web request class, undoubtedly through several private methods that implement MIME content type parsing RFC 2045. These will be hard to test.

Instead of those private methods to parse the headers, I want to defer the parsing to a first class delegate. An actual, red-blooded class that knows only how to parse RFC 2045. Turns out, open source libraries already exist, and I don't have to do the work. A happy side effect.

Thursday, January 28, 2016

Vim gem: built-in calculation

Vim is my go-to editor. Has been for 20 years. Besides being an all-around awesome editor for composing text, it also has some handy built-ins, like calculations:

  • In insert mode, ^R= accepts a mathematical expression, the result of which will be inserted in place.
  • In normal mode, ^A increments the number at (or to the right of) the cursor by one, while ^X decrements it by one. These accept repeats, so 5^A will add 5 to the number.

Thursday, January 21, 2016

Using vim to replace string functions with their multi-byte equivalent

The PHP INI option mbstring.func_overload override certain string functions (like strpos, substr, etc.) with multi-byte aware implementations. This makes it super easy to migrate a legacy code base to UTF-8, but immediately restricts interaction with vendor products (Symfony, Net_DNS2, etc.).

The proper integration is to manually replace string functions with their multi-byte equivalent. In one legacy code base I'm improving, there are on the order of 10k instances of these functions. I want to change and verify each replacement, but I don't want to type much. Time for some vim-fu:
:argdo %s/strpos/mb_strpos/gc | wn
This performs a confirmed find and replace, writes the change to disk, then moves on to the next file.

Thursday, January 14, 2016

Monday, January 11, 2016

[Proposed] Elephpant Etiquette

Yes, I do believe PHP internals needs a guide to etiquette. But, no, not a code of conduct. Internals is a decades (plural) old cathedral-like meritocracy. There is no benevolent dictator. There is no functional oversight group. No rigorous process (like Go has) will work in the internals ecosystem.

Anthony's draft sets the stage, but I don't think it'll draw the crowds. For that, we need a moderate approach that emphasizes the definition of acceptable behavior while limiting the authoritative scope. Here's my second attempt at a custom-fit "code of conduct" roughly based on the one from Go: