Taking Notes

I love taking notes, and I love talking about taking notes. This is how I personally take notes, and I'd love to hear about how you all take notes!

I wrote earlier about why you might take notes. Here's how I take notes:

I've tried a lot of systems. I've tried Evernote. I've tried a wiki (several wikis, really). I've tried text files on the filesystem. I've tried Google Drive. None of these systems worked for me. Their organizational schemes, and the fact that I need a working computer to use them puts too many barriers between me, and the notes I want to write.

The note taking system that works best of me is Bullet Journaling, which is a structured way of categorizing and organizing notes. The practices that I find most useful are adopting a set of shorthand symbols for each note, keeping a weekly log, keeping a monthly calendar and keeping an index of my notes.

How I Take Notes

I take notes at all the meetings and trainings I attend. Sometimes my notes are just "I attended this meeting". Other times, I come out with a list of todos, and still other times I just come out with a bunch of things I want to remember.

Here's an example of notes I took during a project management training:

An example of what notes look like in a bullet journal

I kept track of what the training was about and observations on The Flight of the Phoenix, a training aid our instructor chose. I didn't have any todos or other particularly notable notes on this page, but it gives a flavor of how I use bullet journaling in the day to day.

My Preferred Set of Symbols

I adopted a set of symbols based on the bullet journal suggestions to indicate different types of notes. I went with a subset of the bullet journal symbols, because I found my todos are generally not very complicated. They either need to happen, happened or aren't going to happen. I didn't find moving them around from day to day very useful.

-

I use a dash to prefix general notes and observations

I use an open circle to indicate a todo. I put an "X" through the circle when it's done or cross it out when it becomes irrelevant.

👀

I use an eyeball for things I want to look into more

!

I use an exclamation point for things that I think are important

Keeping a Weekly Log

I work in an office environment, with semi-annual performance reviews. I find writing up what I did over the last six months a lot easier if I've kept track of it at some kind of high level overview. To that end, I use weekly logs

A detailed log of my weekly activities

I write out the log at the start of the week, then as I do things, I put a short note in on each day. "Attended stakeholder meeting," "Wrote workflow container," "Created permit entry UI," "Mentored junior developer," etc.

Keeping a Monthly Log

The weekly logs are great for looking back, but sometimes I want to look forward. I keep a couple months calendar in my journal to help plan when my busy periods are likely to be.

Here's what a fairly empty calendar on a new month looks like:

An empty calendar for a new month

As I learn new things, I put important events in my calendar, whether they are target release dates, testing cycles or demos. I also use these calendars to plan around personal events, like vacations or doctor appointments.

Keeping an Index

Keeping an index sounds boring and difficult, but I find that if I'm diligent, it's not particularly difficult, and it turns out to be one of the most useful parts of my journal.

An index of topics my work bullet journal covers

The three practices that make keeping an index easy are

  1. Number pages as you turn to them
  2. Title new entries before you write them
  3. Update the index as soon as you create a new entry

The payoff is six months after you've written a bunch and can't quite remember where that important technical decision originates from. Just look at the project meetings, or other promising entries and cut your search time down!

Ride off into the Sunset

Whatever method you choose to take notes in, having a system makes it easier to use the notes after writing them. I find the Bullet Journal technique very useful, but it took me a couple tries to settle in on how I wanted to record information. I'm still learning and changing how I take notes - never hurts to change!

Credits

Thank you Matt Reinbold for the notepad

Tags: productivity management process how-to notes journal memo

Get great articles about development every month here ⬇️

Protect Yourself like the FBI — with Memos


Lots of paper

Donald Trump has been in the news a lot lately. Especially concerning FBI investigations and memos. It seems like everyone in Washington is fighting with memos - Comey, McCabe, Nunes - the list goes on and on.

Clearly memos are important, but they also seem distant and removed from programming. You programmers reading this article aren't in a fight with the president or the FBI1, so why should you care about memos?

Tools of Bureaucracy

Printing out memos

You should care about memos because we work in a fundamentally bureaucratic society run by managers2. It's likely that you work for an organization that is large enough to have its own bureaucracy - most programmers do.

If you aren't sure whether you work in a bureaucracy, here's a quick test:

  • Is there a process for getting hired?
  • Is here a process for leaving the company?
  • Is there a process for getting fired?
  • Is there a process for getting a promotion?
  • Does your boss have a boss?

If you answered yes to more than two of them, congratulations! You are a programmer and part-time bureaucrat.

What is a Memo?

Write things in a notebook

It's common for programmers to understand memos in Office Space terms. They are something supervisors do, and they really need TPS coversheets. While this is true3, it is a limiting conception of what a memo is.

Memo is short for "memorandum" which is from the Latin phrase memorandum est, "It must be remembered that..." A memo is some kind of documentation that jogs your memory. Of course, there are many different kinds memos in the modern world - policy memos, memorandum of understandings, meeting minutes, the list goes on.

From day-to-day programmer, the most important kind of memo is the memo-to-file. A memo-to-file is something you write after a conversation or an action that describes what happened. It isn't addressed to anyone, it's not trying to persuade anyone of anything. A memo-to-file simply records an event as you understood it at a certain point in time.

Put another way, a memo-to-file is a note with a date on it.

You should know that memos are being created about you. Your manager, coworkers, and stakeholders are all probably writing things down, about stuff you did!

Hold off on the panic attack. They probably aren't writing down bad things - the vast majority of memos are neutral. "Met with so-and-so, discussed project. Committed to reviewing Jira issues"

For the most part, these memos go unused. Written once, read never. They do, however, come in handy in a variety of situations, usually after something has gone wrong.

Concrete Examples

Filing cabinets full of memos

On the Road to a Job Hunt

Let's start right off the bat with the grim example. Your boss is not happy with your performance and thinks they may have to fire you. In bureaucratic organizations, there is a process connected with firing, and it typically starts with a performance improvement plan4.

Firing is expensive, and it is important to document a repeated pattern of behavior5. Because firing is expensive, many bureaucratic organizations recommend supervisors take actions to head off people getting even a performance improvement plan - mentoring meetings, feedback meetings, checkins, etc.

Supervisors document these informal actions with memos-to-file. "Counseled so-and-so that they need to inform stakeholders of a blown deadline before said deadline is blown." These notes build a case of a repeated pattern of behavior that supports the action they take down the road.

Reviewing What Went Wrong

A less grim occurrence is the post mortem, or after action report. A system failed, and the team wants to figure out what happened and why it failed. Keeping notes about who asked you to do what can be helpful when establishing a timeline. For example, "Chief stakeholder Jane wanted the big feature rolled out next week, so she asked me to do an unscheduled deploy on Friday."

They can also help establish mindset before a system failure - the best post mortems assume everyone is trying their professional best, so knowing why a person thought the action that caused the failure was a good idea is helpful, and memos-to-file can be a useful resource to determine the "why's".

Day-to-Day Disagreements

The most common (and least grim) place that memos-to-file can help a programmer out, is mild disagreement with stakeholders about feature requests and bug fixes. It's common to walk out of a meeting feeling like you have a shared understanding about what to do next, only to find a week or a month later that you did not.

A contemporaneous memo of what you understood as you left the meeting is useful, because it can help you figure out where the disagreement started. They can be useful in figuring out who was 'wrong' but this is of limited usefulness when everyone is on the same team working towards shared goals in a supportive fashion.

In the event that you are working on a contentious team, this type of memo can be useful protection - "See, I did what they said! It's not my fault they changed their mind." Hopefully you are not on a team like this, but even the best teams sometimes veer unexpectedly towards blame and contention.

End of the Year Writeups

Everyone with a boss must, from time to time, say what they did and why it was good for the company. This often comes in the form of an annual performance review with a short write up about what you did and a quick meeting with your boss. These meetings have a high impact on your promotion and bonus potential. It is in your best interest to have a good summary of your accomplishments at hand.

A good collection of memos documenting the people you talked to and the actions you took, as well as the motivations and results, makes quick work of writing end of the year accomplishments.

Be Prepared

A hall of filing cabinets

Write memos, not with the intention of needing them, but with the knowledge that you might need them as circumstances change. "Be Prepared" is an excellent motto, whether you are a scout, a backup-conscientious admin or a programmer-bureaucrat. What is a happy situation today, may turn tomorrow. Memos-to-file can help bail you out when things turn sour.

Happy writing!

Credits

Thank you Kiran Foster for the picture of a filing cabinet Thank you mcfarlandmo for the picture of many filing cabinets Thank you Florian Hufsky for the picture of a notebook Thank you Orin Zebest for the picture of a paper cliff Thank you No More Plains for the picture of a printer

Footnotes

1: Unless you are in a fight with the president and/or the FBI - in that case, I'm interested and want to hear more; dish! 2: Say what you will about James Burnham and The Managerial Revolution, I think there is something to the description of society there 3: Not really. 4: Your organization may call these something else, but the idea is a last chance plan before they show you the door 5: For liability and unemployment insurance reasons both

Tags: career productivity leadership workplace

Get great articles about development every month here ⬇️

Turn Your Spaghetti Code into Functions - Part 3


Start with Part 1 and Part 2 first.

Picking up where we left off in Part II, let's set the stage for using a validator. We left our example elephant in a cool convertible, but let's upgrade his ride to a rocket ship. We'll start like all good rocket scientists - with a round of cleaning!

elephant driving a space shuttle

After mise en place, we will be ready to implement a Validator and Result, which will create a single, standard, expandable format for business rules and an api for consuming results from business rule queries. No more guessing what a rule-compliant call looks like! No more stringing together crystal towers of nested boolean logic!

Cleanup

There are a couple places where we used .and() to combine predicates in an if block. Replace those with an independent predicate, so all if blocks refer to only one predicate

static final Predicate<WidgetTransfer> suffientAmount = trans -> trans.getTransferer().getAccount(trans.getFromAccount()).getBalance().compareTo(trans.getAmount()) < 0;
    static final Predicate<WidgetTransfer> isPartner = trans -> trans.getTransferTypeCode().equals("200");
    static final Predicate<WidgetTransfer> isFriendsAndFamily = trans -> trans.getTransferTypeCode().equals("710");
    static final Predicate<WidgetTransfer> isFriendAndFamilyDiscountLegal = trans -> trans.getAreaCode().matches("574|213|363|510");
    static final Predicate<WidgetTransfer> isPartneringArea = trans -> trans.getAreaCode().matches("907|412|213");
    static final Predicate<WidgetTransfer> isDirigibleForbiddenArea = trans -> trans.getAreaCode().matches("213");
    static final Predicate<WidgetTransfer> isDirigibleCategory = trans -> trans.getTransferer().getCategory().equals("D");
    static final Predicate<WidgetTransfer> isInternal = trans -> trans.getTypeCode().equals("I");
    static final Predicate<WidgetTransfer> isBlockSize = trans -> isBlockSize(trans);
    static final Predicate<WidgetTransfer> isTotalOverCap = trans -> isTotalOverCap(trans);

    static final Predicate<WidgetTransfer> parterTransferReqs = trans -> isPartner.and(isPartneringArea.negate()).test(trans);
    static final Predicate<WidgetTransfer> dirigibleTransferReqs = trans -> isPartner.and(isDirigibleForbiddenArea.negate()).and(isDirigibleCategory).test(trans);
    static final Predicate<WidgetTransfer> friendsAndFamilyReqs = trans -> isFriendsAndFamily.and(isFriendAndFamilyDiscountLegal.negate()).test(trans);
    static final Predicate<WidgetTransfer> internalBlockReqs = trans -> isInternal.and(isBlockSize).test(trans);
    static final Predicate<WidgetTransfer> internalTotalCapReqs = trans -> isInternal.and(isTotalOverCap).test(trans);

    public static final String checkWidgetTransfer(WidgetTransfer transfer) {
        String businessRuleErrors = "";

        if (suffientAmount.test(transfer)) {
            businessRuleErrors += "Insufficient balance to transfer ; ";
        }

        if (parterTransferReqs.test(transfer)) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }

        if (dirigibleTransferReqs.test(transfer)) {
            businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
        }

        if (friendsAndFamilyReqs.test(transfer)) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }

        if (internalBlockReqs.test(transfer)) {
            businessRuleErrors += "Amount is too small for I type transfer. ; ";
        }

        if (internalTotalCapReqs.test(transfer)) {
            businessRuleErrors += "This transfer is too large. ; ";
        }

        return businessRuleErrors;
    }

The above code has a lot of boiler plate - but it's boiler plate that is invisible to the average Java dev. All those if blocks are boiler plate. You have to type them again, and again. What if I told you, you could use a validator and clean up all your business rules? Think of the miles of conditionals in your business code, and imagine each reduced to a single composable function call.

Boilerplate Removal

Another problem is that checkWidgetTransfer returns a string. This pushes the responsibility for determining if an error has occured onto the calling method. All checkWidgetTransfer callers need a section that looks like this:

String result = checkWidgetTransfer(transfer);
if(null == result || 0 == result.size()) {
    //continue
}else{
    handleError(result);
}

Multiply this by every bizarre process that Bob in accounting, Carol in sales uses and Duane in HQ uses. It can get.... big.

We can save on typing, share conditionals and share business logic by using the Validator and Result technique. Here's what it looks like from the caller's perspective:

checkWidgetTransfer(transfer).onError(err -> handleError(err));
//continue

This provides an api for the caller that indicates what an error condition is. Callers no longer have to guess that an empty string is a pass, and a non-empty string is a fail. The api provides a place to put error handling, which can take an existing function, or have an on-the-fly lambda in place. Sweet!

You still have to write up Bob, Carol and Duane's favorite workflow. but things are compact and you don't have to go down many twisty branches, each more a like than the last.

Now with a Validator

What if you didn't have to write any if/then/else statements? What if you you only had to write the logic, and something else would handle stringing that logic together. A validator can make that possible.

Here's what the implementation of checkWidgetTransfer looks like, using a validator:

public static final Result<WidgetTransfer> checkWidgetTransfer(WidgetTransfer transfer) {
    Validator<WidgetTransfer> widgetTransferValidator = new Validator();
    widgetTransferValidator.addRule(suffientAmount, "Insufficient balance to transfer");
    widgetTransferValidator.addRule(parterTransferReqs, "This area is not a transfer eligible area");
    widgetTransferValidator.addRule(dirigibleTransferReqs, "D Category Transferer can only be transferred in transfer area 213");
    widgetTransferValidator.addRule(friendsAndFamilyReqs, "This area is not an eligible area");
    widgetTransferValidator.addRule(internalBlockReqs, "Amount is too small for I type transfer");
    widgetTransferValidator.addRule(internalTotalCapReqs, "This transfer is too large");
    return widgetTransferValidator.validate(transfer); 
}

The validator can take as many rules as needed, and each rule gets a name and a matching message. The validator ensures every rule is applied to the transfer, and a Result is returned, containing either the error messages or the transfer.

Implementation Details

I implemented a Validator as a HashMap of functions to error strings. The validate method tests each function, and if the test is true, collects the matching message in the Result.

I implemented Result as an Either. Callers have the option of getting a boolean hasErrors or passing in a Consumer to onError. The important thing about a Result is that it is never null and it guides the developer to the correct method of handling an error.

Wrap up

With just a tiny push, we helped our elephant reach orbit. We've gone from bog standard spaghetti business logic to pure functions embodying composable business logic. Thank you for coming along for ride. I welcome comments or feedback at gunnar@gunnargissel.com, or @monknomo on Twitter

Credits

Thank you Oliver Dodd for the elephant

Thank you NASA, ESA, N. Smith (U. California, Berkeley) et al., and The Hubble Heritage Team (STScI/AURA) for the Carina Nebula

Photo of the shuttle Endeavour by Mr. Littlehand

Boiler Plate from Les Chatfield

Blastoff by Matthew Lancaster

Tags: java technique diy how-to tutorial guide lambda function refactor

Get great articles about development every month here ⬇️

Java 8 Stream Cheatsheet


Streams are a way of working with objects in a collection.

Streams allow a person to say what they want to have at the end. Contrast that with the imperative approach that requires a person to say how to collect what they want to have at the end.

Streams also allow for trivial multi-threading.

Basic Stream Methods

There are a couple basic methods that govern stream interaction. The general idea behind these methods is manipulating a collection of objects so that the only objects remaining are the ones you want.

If I had to pick the indispensable methods for working with a Stream, I'd say Map, Filter, and Collect are on it.

Filter Filter takes a Predicate, and leaves only the values that the Predicate is true for.

Map Map takes a Function, and transforms the values in the stream from one type to another

Sorted Sorted takes a Comparator, and orders the values in the stream according to the ordering specified in the Comparator.

Collect Collect stops the stream and puts the values from the stream into some kind of Collection, like a List.

toArray toArray() stops the stream and returns an array with the elements contained in the stream.

Finders

A common task, when using a collection, is to get an element out of the stream. These are generally used after Filter and Map, to guarantee that the element you find matches some criteria and is of the preferred type.

findAny() findAny() returns an Optional containing an element in the stream (or nothing, if the stream is empty). Order is not respected here, especially in parallel stream situations.

findFirst() findFirst() returns an Optional containing the first element in the stream (or nothing, if the stream is empty). Order is respected when using this method

max(Comparator comparator) max returns the maximum element of the stream for the given Comparator

min(Comparator comparator) min returns the minimum element of the stream for the given Comparator

Matchers

Another common task is to determine if the objects meet some given criteria. These "matcher" methods indicate whether some, none or all the objects in a stream meet a given criteria.

allMatch(Predicate pred) Returns true if all elements in the stream match the Predicate

anyMatch(Predicate pred) Returns true if any elements in the stream match the Predicate

noneMatch(Predicate pred) Returns true if no elements in the stream match the Predicate

Stream Modifiers

From time to time, a stream is not quite the "right" stream. These methods create a new stream with slightly different characteristics than the old stream, whether it is a different length, different starting point, or a guarantee that only unique objects are contained.

distinct() distinct() creates a new stream that has only distinct elements (based on .equals) in it

limit(int maxSize) limit(maxSize) creates a new stream by truncating the original stream to be no longer than maxSize.

skip(long n) Skips the first n elements of the stream, and creates a new stream out of the rest

sorted() Creates a new stream, where the elements are sorted according to natural order

sorted(Comparator comparator) Creates a new stream, where the elements are sorted according to the Comparator

Stream Characterizers

Another common task, when working with collections, is to determine some characteristics about the collection as a whole. The stream api provides a method to figure out how big a given stream is.

count() count() counts the number of elements in the stream

Tags: java8 programming cheatsheet guide how-to tutorial

Get great articles about development every month here ⬇️

Newb Valley


There comes a point when you are good at what you do. If you're like me, you've been pretty good at things for a long time - pretty good at school, pretty good at work, pretty good at structured environments that provide a path and use skills you already know. This is a very comfortable place to inhabit. It comes with respect, both self respect, and respect from your peers and colleagues.

As long as you stay in your structured environment, on the path that uses/improves your existing skills, getting better feels easy - or at least, predictable. You put in effort, your skills improve in a (more or less) linear fashion.

Sooner or later, you may have the desire to learn something outside of your existing expert skillset. If you're like me, you are surprised when this hurts. Suddenly, you are not good at what you are trying to do. The shock of a drop in your skill level is disconcerting, and the temptation to retreat back to the comfort of your old groove is strong.

To pick a concrete example, you may be a talented developer. You can pick up new languages, or build new systems. If there is something programming-related you don't know, you have a good idea how long it will take to learn it, and how you should go about learning it.

But one day you become a lead developer, or transition into the management. Suddenly, you need skills that you haven't practiced for years and don't have a clear path from where you are.

Welcome to Newb Valley!

It's the difference between your hoped for path and your actual path that causes a lot of pain. There are a couple things you can do to reduce your pain and power through Newb Valley

  1. Know it is coming
  2. Have a plan to bridge the gap
  3. Forgive yourself for mistakes

Know it is Coming

This is an easy, but critical step. If your new found lack of competence surprises you, the impulse to pack it in and retreat to comfort may prove overwhelming. Instead, lean in to your newb-ness. Embrace your lack of competence - after all, if you knew what to do, you wouldn't be learning.

Developing a level of comfort with uncomfortable situations will go far.

Have a Plan

One way of developing and keeping comfort in the face of uncomfortable situations is to have a plan to lean on. You know you're in for an uphill struggle, so figure out in advance how you want to climb that hill. Give yourself some smaller goals and ways to check on your progress.

Forgive Yourself

You will be less competent for some amount of time.

That means you'll make mistakes. You'll blow deadlines. You'll deliver less scope than you had planned. Your plan to progress to the next level will be wrong.

Take a breath, re-evaluate, learn a lesson and move on.

You'll climb out of Newb Valley before you know it

Credits

Thank you Sandy Brown Jensen for the picture of the mountain climbers

Tags: leadership diagram learning

Get great articles about development every month here ⬇️

Turn Your Spaghetti Code into Functions - Part 2


Read Part 1, first.

In Part 1, we started with an example of common business logic code, and an analogy based on cramming an elephant into a Smart Car. We did some refactoring to untangle the nested if/else blocks, but we left after we finished cramming the elephant into a Smart Car.

In many ways, it feels "good enough", but what if I told you we can get it better? Java 8 brings us a new tool to contain and use the logic within an if statement - the Predicate. In terms of elephants driving cars, we can get it driving a stylish convertible.

So you have a lot of conditional logic, and you find yourself copy-pasting conditions from one logic block to the other. It's easy, it's seductive, but it's wrong. Copy pasting is error prone and extra work! If you're like me, you try to work as little as possible - that's the computer's job. Java 8 provides a new tool to prevent copy-pasting and keep your code DRY.

Using Predicates allows us to encapsulate logic as a variable. This makes for two features that are particularly powerful

  1. Variable names communicate the intent of the logic
  2. Logic is resusable and individually testable

And Now, With Predicates

Before Java 8, I wasn't tuned in to the functional programming world. The last time I remember hearing about Predicates was in school when I was ignoring a grammar lesson. It turns out that, when programming, Predicates can really improve your code.

A grammar school

Here we will make the if/else blocks more readable by creating Predicates out of the logic they represent. Predicates can be named, which allows developers to name rules in a way that allows for clear discussions with even non-technical users.

static final Predicate<WidgetTransfer> suffientAmount = trans -> trans.getTransferer().getAccount(trans.getFromAccount()).getBalance().compareTo(trans.getAmount()) > 0;
static final Predicate<String> isPartner = ttc -> ttc.equals("200");
static final Predicate<String> isFriendsAndFamily = ttc -> ttc.equals("710");
static final Predicate<String> isFriendAndFamilyDiscountLegal = ac -> ac.matches("574|213|363|510");
static final Predicate<String> isPartneringArea = ac -> ac.matches("907|412|213");
static final Predicate<String> isDirigibleArea = ac -> ac.matches("213");
static final Predicate<String> isDirigibleCategory = cat -> cat.equals("D");
static final Predicate<String> isInternal = tc -> tc.equals("I");

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    String transferTypeCode = transfer.getTransferTypeCode();
    String areaCode = transfer.getAreaCode();
    String category = transfer.getTransferer().getCategory();
    String typeCode = transfer.getTypeCode();

    if (suffientAmount.test(transfer)) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }

    if (isPartner.test(transferTypeCode)
            && isPartneringArea.negate().test(areaCode)) {
        businessRuleErrors += "This area is not a transfer eligible area. ; ";
    }

    if (isPartner.test(transferTypeCode)
            && isDirigibleArea.test(areaCode)
            && isDirigibleCategory.test(category)) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }

    if (isFriendsAndFamily.test(transferTypeCode)
            && isFriendAndFamilyDiscountLegal.negate().test(areaCode)) {
        businessRuleErrors += "This area is not an eligible area. ; ";

    }

    if (isInternal.negate().test(typeCode)
            && !isBlockSize(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }

    if (isInternal.negate().test(typeCode)
            && isTotalOverCap(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • Each if block is readable in something approximating "business English"
  • The rules are defined as Predicates
  • The rules are portable and reusable.
  • The rules are also individually testable, without having to test each branch at once

The Bad

  • This technique still uses && which is not idiomatic with functions in Java
  • We are forced to use && because the Predicates take different types of objects, so we can't chain them together
  • While the Predicates that make up the rules are portable, the rules themselves are made of multiple Predicates and are not portable
  • Nothing has been done that can't be done with ordinary methods
  • Good old public boolean isSufficientAmount(String amount) would suffice
  • We still have to create all these property variables to get the appropriate values to give to our Predicates

Predicate Chaining

Let's fix some of the stuff on the 'bad' list from the previous example.

We can get rid of && by using just a little bit more of the functional interface and refactoring the Predicates to all take the same type of object, in this case a WidgetTransfer object. The goal is to make our Predicates like legos - interlocking and interchangeable.

lego elephant

static final Predicate<WidgetTransfer> suffientAmount = trans -> trans.getTransferer().getAccount(trans.getFromAccount()).getBalance().compareTo(trans.getAmount()) > 0;
static final Predicate<WidgetTransfer> isPartner = trans -> trans.getTransferTypeCode().equals("200");
static final Predicate<WidgetTransfer> isFriendsAndFamily = trans -> trans.getTransferTypeCode().equals("710");
static final Predicate<WidgetTransfer> isFriendAndFamilyDiscountLegal = trans -> trans.getAreaCode().matches("574|213|363|510");
static final Predicate<WidgetTransfer> isPartneringArea = trans -> trans.getAreaCode().matches("907|412|213");
static final Predicate<WidgetTransfer> isDirigibleArea = trans -> trans.getAreaCode().matches("213");
static final Predicate<WidgetTransfer> isDirigibleCategory = trans -> trans.getTransferer().getCategory().equals("D");
static final Predicate<WidgetTransfer> isInternal = trans -> trans.getTypeCode().equals("I");
static final Predicate<WidgetTransfer> isBlockSize = trans -> isBlockSize(trans);
static final Predicate<WidgetTransfer> isTotalOverCap = trans -> isTotalOverCap(trans);

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    if (suffientAmount.test(transfer)) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }

    if (isPartner.and(isPartneringArea.negate()).test(transfer)) {
        businessRuleErrors += "This area is not a transfer eligible area. ; ";
    }

    if (isPartner.and(isDirigibleArea).and(isDirigibleCategory).test(transfer)) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }

    if (isFriendsAndFamily.and(isFriendAndFamilyDiscountLegal.negate()).test(transfer)) {
        businessRuleErrors += "This area is not an eligible area. ; ";
    }

    if (isInternal.negate().and(isBlockSize.negate()).test(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }

    if (isInternal.negate().and(isTotalOverCap.negate()).test(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • We get rid of extra variables that hold string values from the WidgetTransfer object
  • We compact our if-blocks while retaining readability
  • We only evaluate one type of object

The Bad

There is very little bad about this refactor point. The conditionals are all very easy to read. It's clear what each rule is, and what branch. If I didn't know what I have planned for the next article, I would be satisfied to stop here.

Next Steps

elephant driving a convertible citroen

All our rules are Predicates and each rule takes the same kind of object, a WidgetTransfer. That makes our rule composable in the fashion demonstrated above, but there are some improvements we can make to how we compose the business rules.

The first improvement is to combine small rules into larger rules - we are doing this in conditional statements, but it is just as easy to do so in a Predicate. We can also create a Validator object to create a collection of business rules and error messages. A Validator dispenses with the need to create a complex nested if/else logic structure, and is a concrete unit of business logic that can be shared, re-used and tested.

Sign up for my email list to get notifcations about updates in continuing series, and a monthly digest of interesting programming and tech leadership articles

We will go over using Validators in the to-be-written Turn Your Spaghetti Code into Functions, Part III

Credits

Thank you roebot for the left facing Smart Car

Thank you Oliver Dodd for the elephant

Thank you Phillip Pessar for the convertible

Thank you JPL and NASA/Space Telescope Science Institute for the edge-on galaxy picture

Thank you Philip Sheldrak for the grammar school

Tags: java technique diy how-to tutorial guide lambda function refactor

Get great articles about development every month here ⬇️

3 Phases for Building a Culture of Learning


I attended Joseph Matsey's talk on building a culture of learning at Abstraction's last year. I really enjoyed the talk, and have been thinking about and implementing ideas from it over the past year. Here's a summary of my takeaway for the 3 different phases of culture building (with a bonus 4th phase, of "you have a culture")

Guerilla Learning

This is where a lot of places start - there isn't really any particular culture of learning, but some learning exercises and activities are supported

  • High resistance to taking time from shipping
  • Ad hoc learning
  • Onboarding by oral tradition
  • Few folks driving learning
  • Approach
    • Think opportunistically
    • Improve team members primary roles
    • Seek fast returns with low investment
    • Examples
      • Workshops
      • Meetups
      • Build a team library
      • New hire buddies
        • Try cross-team buddies
    • Dangers
      • Buy in never starts
      • Stay stuck in guerilla learning forever

Buy in Starts

This is a dangerous intermediate phase, where some culture building has happened, but it's not baked in or fully supported by management

  • New hires have some kind of organized learning
  • Team members want more, but aren't empowered
  • Approach
    • Leverage internal experts (workshops, talks)
    • Seed good role models (and shame everyone else)
    • Mentor mentors (mentorship is an orthogonal skillset to what you do at work)
    • Examples
      • Weekly talks, 4 15 minute talks
      • Project updates
      • Tech approaches
      • Senior leadership talks
      • Ops talks
    • Dangers
      • Key folks leaving
      • "Takes too much time" payout not obvious, track and figure out how to measure returns
      • Mentor burnout

Making it Stick

This is the final phase, that takes management buy in, re-programming existing employees and enculturating new hires

  • Boringly normal
    • From "workshops are amazing" to "oh god a workshop"
  • Approach
    • Momentum to sustain effort
    • Sustained learning
    • Make people forget the old way
    • Examples
      • Learning/teaching as a KIP
      • Apprenticeships
      • 3 part onboarding
        • Teach skills
        • Teach processes, history and context
        • Personal development
      • 20% time
    • Dangers
      • Payoffs hard to measure
      • No one is responsible
      • Losing the habit

Self Sustaining

At some critical mass point, it continues lurching forward. This point is when you have a durable culture of learning

Credits

Thank you Mat the W for the photo of Hammerschlag

Tags: leadership culture learning notes talks conferences

Get great articles about development every month here ⬇️

Turn Your Spaghetti Code into Functions - Part 1


Developers can sink a lot of hours into fighting business rule code. Spaghetti business rules make it so little changes need to be copy pasted repeatedly throughout if/else blocks. It's like trying to shoehorn an elephant into a Smart Car, when it should be like snapping together legos.

Anyone who has worked on a 'mature' set of business rules knows that untangling what is going on is extremely hard. If cramming the elephant into the Smart Car was tough, getting it out is an order of magnitude tougher. I'm going to show how to make your Smart Car-driving elephant a little happier.

Business rules are your money-makers and form the essence of your business's being. They also change a lot. This dynamic often leads to rushed work in business rules and over the long run creates a nasty mess right in the heart of your money maker. A nasty mess is hard to read and even harder to figure out what it is supposed to be doing.

Further, the OO way of encapsulating business rules is either fundamentally inadequate, or so widely misunderstood by working developers that the common pattern is a god class that mutates everything it touches. Testing a huge class, stuffed with spaghetti and mutators is daunting, at best, impossible at worst. No tests make it very hard to develop without fear.

Here's a set of techniques for making the code easier to follow, so you can put your effort into understanding your stakeholders and making sure what's in the code is what they want to be in the code.

This will teach you how to untangle business rules, so you can easily work with them.

Readability

Deep nesting impairs readability. Even when developers use tabs well and are consistent with their curly brace usage, around the 3rd layer of if/else statements, it starts getting hard to tell what particular branch you are reading.

Is this the one with the null child object? I'll just check for null again here really quick

Avoid this kind of impulse by structuring your code so there is never any doubt about what you are checking.

Long conditions that reach deep into child or grandchild objects also impair readability. Stakeholders never say (unless they've been terribly abused) something like:

If the transfer type code is 200 and it's not in areas 907, 412 or 213, then it's not allowed

  1. That's got more "nots" than a human likes to say
  2. Humans don't usually go on about codes.

They might say something like:

If it's a partner transaction, we only allow it in partner areas

If your code takes mental backflips to match how your stakeholders talk, you're gonna have a bad time. So are they, and they won't even know why.

Testability

Gnarly business rule code is hard to test. The twists and turns of deeply nested if/else statements are easy to get lost in, easy to forget a branch and sometimes just plain impossible to set up in a testing harness. No tests mean every change is dangerous - you are one misplaced }, or backwards > from giving your users a headache.

Gnarly business rule code seems to encourage devs to mutate the objects it is validating. I don't know why this is, but I've seen it many times in the wild. State mutation amps up the testing difficulty by an order of magnitude. Now, not only must you test for messages and expected errors, but you must detect changes to the object you are validating. Are they intentional? Are they correct?

Who knows? I promise no one wrote it down.

Response speed

Stakeholders, users, managers and developers constantly engage in timeline tug of war. Technical debt hamstrings developers' ability to give their users what they need. A good 'tell' that your system has a lot of technical debt is users can easily describe a feature, but developers can't easily implement it. The simplest things turn into month long bushwhacking exercises.

To me, nothing says bushwhacking like coming down a mountain, getting off trail and fighting through Devil's Club and loose scree -

Wait. I meant nothing says bushwhacking like coming into a method and getting on the wrong nested if/else branch and fixing something that wasn't broken in the first place. Or noticing that 100 lines of code have never, ever been executed, because they wouldn't work. Or any number of the fun surprises that spaghetti-fied business rules bring.

Composability

Composability lets you create new rules out of old rules. Most new business rules have a lot in common with existing rules. The ability to combine existing rules and add that one special case is extremely powerful. Your stakeholders will be amazed at your turnaround time!

The spaghetti style is anti-composable. Copy-pasting, duplicating code and mangling switch statements are practically requirements with spaghetti-ed business rules. Good luck re-using something to get at the one

Example Bad Code

Here's an example of standard Java business logic that evaluates a business object (BusinessTransfer) and creates messages to return to the user if it violates business rules:

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    if (transfer.getTransferer().getAccount(transfer.getFromAccount()).getBalance().compareTo(transfer.getAmount()) < 0) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }


    if (transfer.getTransferTypeCode().equals("200")) {
        if (!transfer.getAreaCode().matches("907|412|213")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        } else if (!transfer.getAreaCode().matches("213")) {
            if (transfer.getTransferer().getCategory().equals("D")) {
                businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
            }
        }
    } else if (transfer.getTransferTypeCode().equals("710")) {
        if (!transfer.getAreaCode().matches("574|213|363|510")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }
    }


    if (transfer.getTypeCode().equals("I")) {
        if (isBlockSize(transfer)) {
            businessRuleErrors += "Amount is too small for I type transfer. ; ";
        }
        if (isTotalOverCap(transfer)) {
            businessRuleErrors += "This transfer is too large. ; ";
        }
    }

    return businessRuleErrors;
}


public static boolean isBlockSize(WidgetTransfer transfer) {
    return transfer.getAmount().compareTo(1000) < 0;
}


public static boolean isTotalOverCap(WidgetTransfer transfer) {
    return transfer.getAmount().compareTo(1000000) > 0;
}

The above example is inspired by actual code running in the wild. What I show here is simplified, and anonymized. It's as hard to read as the original.

Reading it takes knowledge of what a "200" transfer type code is or what is acceptable data for different transfer area codes. The parentheses are nested, which makes copy-pasting (a common technique for working with this style of code) perilous. A dev can't afford to miss a single curly bracket without causing a hard-to-debug problem.

Logic Block Paradigm Shift

A quick way of refactoring long branching if/else code is to dispense with branches and with elses. By rephrasing each business rule into a positive constraint, a developer can check to see if the constraint conditions are met, rather than walking a branching logic tree. This technique increases line count a little, but improves readability a lot.

Another low hanging fruit is to create instance variables to hold values, rather than use getters (or worse, nested getters!). This gives the ability to name what a thing is in the context you are using it in, rather than relying on getters to have a good name in your context.

public static final String checkWidgetTransfer(WidgetTransfer transfer ) {
    String businessRuleErrors = "";
    Integer balance = transfer.getTransferer().getAccount(transfer.getFromAccount()).getBalance();
    Integer transferAmount = transfer.getAmount();
    String transferTypeCode = transfer.getTransferTypeCode();
    String areaCode = transfer.getAreaCode();
    String category = transfer.getTransferer().getCategory();
    String typeCode = transfer.getTypeCode();


    if (balance.compareTo(transferAmount) > 0) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }


    {
        if (transferTypeCode.equals("200")
                && !areaCode.matches("907|412|213")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }
    }


    if (transferTypeCode.equals("200")
            && areaCode.matches("213")
            && category.equals("D")) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }


    if (transferTypeCode.equals("710")
            && !areaCode.matches("574|213|363|510")) {
        businessRuleErrors += "This area is not an eligible area. ; ";


    }


    if (!typeCode.equals("I")
            && !isBlockSize(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }


    if (!typeCode.equals("I")
            && isTotalOverCap(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • The above code is much more readable.
    • Each business rule is contained in its own block of logic
    • All the properties needed to determine whether a condition has been met are named.
    • You can describe each business rule as written and it very nearly sounds like English.
  • The logic blocks are small and discrete.
    • There are no nested curly braces, so you'll have a hard time getting lost in the code.

The Bad

  • Business knowledge of what codes mean is still required
    • What is a "200" transfer type code? The code doesn't say, so hopefully it is documented somewhere...
  • Negative conditionals abound - if (!typeCode.equals("I"))
    • Negative conditionals are a little hard to say, and are harder to reason with than positive conditionals.
  • Still mutating those businessErrorMessages
    • This is just one more thing to set up in a unit test

What's Next?

I'll continue this refactoring discussion in a couple future posts. Check out my example refactoring repository for a preview!

We're going to see:

  • How to use Predicates to make your life better
  • Using actual objects, and not just their properties, to make your life better
  • Using Functions and validator objects, to make your life better

We'll try to get that elephant in his Smart Car, but mostly we'll try to make your life better.

Credits

Thank you Volantra for the right facing Smart Car Pic

Thank you roebot for the left facing Smart Car

Thank you Oliver Dodd for the elephant

Thank you Neils Heidenreich for the mutant strawberry

Thank you Peter Stevens for the Devil's Club

Thank you to Erica Schoonmaker for the picture of the books

Tags: java technique diy how-to tutorial guide lambda function refactor

Get great articles about development every month here ⬇️

Footnotes!


I was reading away on the internet when I saw a cool thing. Footnotes that popup when you hover over the little 1 note.

Lukas Mathis generously offered his code to the public, so I borrowed the code in his bookmarklet for Daring Fireball. I wasn't quite doing Gruber-style footnotes, which the code assumes.

Gruber Style Footnotes

A Gruber-style footnote has two parts:

  1. The superscript link
  2. The footnote with a return link

The superscript link, in Markdown, looks like this: <sup id="fnr-footnotes-1">[1](#fn-footnotes-1)</sup>. The important parts are that the <sup> element has an id that starts with "fnr" (for "footnote return", I assume), and a link to the footnote that starts with "fn".

The footnote with a return link looks like: <a id="fn-footnotes-1">1: </a> This is an example footnote here - see how it has a return link [⏎](#fnr-footnotes-1). The important parts are the link with an id that starts with "fn" and the return link to the superscript link's <sup> element.

The script I borrowed assumes you have both parts of the Gruber-style footnote, with the appropriate prefixes and return links.

I adjusted the script's color a little to match my theme, but here it is, more or less unchanged from Lukas Mathis' original:

$(document).ready(function() {
  var sups = document.getElementsByTagName("sup");
  var footnotehtml = [];
  for (var i = 0; i < sups.length; i++) {
    var sup = sups[i];
    if (sup["id"] && sup["id"].substr(0, 3) == "fnr") {

      var footnr = sup["id"].substr(3);
      console.log(footnr);
      var footnote = document.getElementById("fn" + footnr);
      console.log(footnote);
      if (!footnote) continue;
      console.log("asdfasdfaf");
      footnotehtml[i] = footnote.parentNode.innerHTML;
      console.log(sup);
      sup.setAttribute("footnoteindex", i);
      sup.onmouseover = function(event) {
        var footnotepopup = document.getElementById("footnotepopup");
        if (footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
        var index = parseInt(this.getAttribute("footnoteindex"));
        var popup = document.createElement("div");
        popup.innerHTML = footnotehtml[index];
        popup.id = "footnotepopup";
        popup.style.position = "absolute";
        popup.style.left = event.pageX - 125 + "px";
        popup.style.top = event.pageY + 25 + "px";
        popup.style.width = "15em";
        popup.style.textAlign = "left";
        popup.style.backgroundColor = "Gainsboro";
        popup.style.border = ".1em solid black";
        popup.style.borderRadius = "6px";
        popup.style.padding = "1em";
        document.body.appendChild(popup);
      };
      sup.onmouseout = function(event) {
        var footnotepopup = document.getElementById("footnotepopup");
        if (footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
      };
    }
  }
});

Footnotes

1: This is an example footnote here - see how it has a return link

Credits

Thank you to Erica Schoonmaker for the picture of the books

Tags: javascript design insidebaseball

Get great articles about development every month here ⬇️

Changing an Uncontrolled Input to Controlled Error in React


React wants form inputs and the like to keep their state inside React, inside of inside the input. This style is called a "controlled input". They also offer "uncontrolled input" where the input manages its own state.

React apparently does not like inputs switching from controlled to uncontrolled. I was developing a form with the docs open next to my editor and I kept getting a Warning: CustomInput is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info:https://fb.me/react-controlled-components

As far as I could tell, my input was set up to always be controlled. I changed javascript around and googled for quite some time before realization set in

I was Switching a Form Value from '' to null

This is apparently a no-no in React's controlled component land. My initial state set up had all empty strings, and the only subsequent state manipulation was to get data from webservices. The webservices I'm working with return a fairly faithful representation of data from a database - nulls and all.

I solved it by converting my state variable to const in the render method - with a twist:

const nullable = this.state.nullable == null ? '' : this.state.nullable;

A little sprinkling of last minute converters and all is well.

Thank you Sebastian Dooris for the sprinkles

Tags: react newb learning

Get great articles about development every month here ⬇️

Page 1 / 4 »