Blog logotrial and stderr

Brandon Istenes

Linting uninitialized field dereference in Java

 •  Filed under java

Am I out of my mind for thinking that some linter or other should be able to recognize the following code as buggy?

package com.company;

public class Main {

    public String greeting = "hey";

    private Main anotherMain;

    public Main getAnotherMain() {
        return anotherMain;
    }

    public void setAnotherMain(Main main) {
        this.anotherMain = main;
    }

    public static void main(String[] args) {
        Main main = new Main();
        System.out.println(main.getAnotherMain().greeting);
    }
}

I expect a linter to tell me that the unchecked dereference main.getAnotherMain().greeting could produce an NPE because anotherMain defaults to null.

I tried SpotBugs, NullAway, and the IntelliJ static checker, and none of them can catch this. Bug filed against SpotBugs. We'll see how it goes.

Choose unique names for test fixtures

 •  Filed under testing, art

In a library I maintain, I wrote all the tests using names from the foo-bar-baz family in fixtures.

I realized that having 50 tests where all the test data is foo-bar-baz makes it harder to debug. Adding a console.log to a line that gets hit 50 times might yield variations on { foo: "bar" } for all 50 tests, but only one corresponds to the error I'm interested.

That's why I've switched to pulling random names from a less limited domain. For this library, I'm using Japanese food names. It doesn't matter what they are or what they mean. What matters is that I can search my test logs for tamago and, if I've only used tamago in one test, immediately find the data corresonding to the test I'm interested in, { tamago: { kake: "gohan" } }. Delicious.

NPE

 •  Filed under java

Type safety without null safety is like a biohazard suit with an open face.

This is very typical Java code. It exhibits obnoxiously verbose typing, and yet can crash your application with NullPointerExceptions at runtime.

SuperDatabaseConnector superDatabaseConnector = SuperDatabaseConnector.getInstance();

getResultsFrom(superDatabaseConnector);

public static SomethingElseButMaybeNull getResultsFrom(SuperDatabaseConnector superDatabaseConnector) {
  return superDatabaseConnector.getResults();
}

Java is a bad language and you shouldn't use it.

Merging packages and their histories into a Lerna monorepo

 •  Filed under git

Update: You probably just want to use lerna import. But if you'd like a manual technique, or aren't actually using Lerna, read on. Let's say you have a Lerna monorepo called acme. You have a package called app, presently in its own repository, that you want to move into repo. read on...

My git workflow

 •  Filed under git

This is my main workflow when using git.

(master)$ git pull
(master)$ git checkout -b my-feature
# make changes
(my-feature)$ git add [changed files]
(my-feature)$ git commit
# repeat the above as needed
(my-feature)$ git fetch origin
(my-feature)$ git rebase origin/master  # try --interactive some time, it's fun
(my-feature)$ git push [origin|upstream|fork] my-feature
# open a PR
# make some fixes
(my-feature)$ git add [changed files]
(my-feature)$ git commit
(my-feature)$ git push [origin|upstream|fork] my-feature
# uh-oh, need to resolve conflicts with master!
(my-feature)$ git fetch
(my-feature)$ git rebase origin/master --interactive
# fix conflicts
(my-feature)$ git rebase --continue
(my-feature)$ git push [origin|upstream|fork] my-feature --force  # never force master
# ok, my PR's merged, all done with that branch!
(my-feature)$ git checkout master
(master)$ git pull
# and we begin again

I've regularly been seeing help threads from people who somehow wind up hundreds of commits "ahead" of master. I don't know how you get git into such a mess, but hopefully the workflow above helps more people avoid it.

Factoring out and bad abstractions

 •  Filed under art

Something or other has lead me to reading a lot about bad abstractions lately, including a famous article where Sandi Metz writes

duplication is far cheaper than the wrong abstraction

I want to add that it's not just a matter of choosing between two cognizable options. It's that all programmers chronically underestimate the difficulty of inventing new abstractions. We need to take that into account in deciding whether to take the leap in the first place.

I want to reframe the idea of "factoring out common code" and "avoiding copy-pasting." Every time you do one of these simple-sounding things, you are actually inventing an abstraction. You are engaging in the most intellectually demanding act of software engineering. It involves balancing the predicted cognitive load which exists outside, via, and inside your abstraction. It requires you to predict the future about business requirements and code that is yet to be written. On top of all that, it is a test of your mastery of technical jargon, of grammatical norms in programming, and of the English language itself.

Naming has been compared in difficulty to cache invalidation, and yet I don't seem to be surrounded by half-baked attempts at cache implementations.

When Kent C. Dodds says to Avoid Hasty Abstractions, remember that he is a famous expert programmer. "Not hasty" for him sets a pretty high bar for you and me.

Instant https localhost fileserver

 • 

This is something that Browsersync does  (among probably a lot of other things).

Install with

npm i -g browser-sync

Serve the contents of the current directory with

browser-sync start -s --https

No setup required. See the docs for other features. You probably want the --cors flag.