Anecdotal Evidence

I’ve been having a conversation with some folks on Twitter about their evidence that mob programming makes Brooks’ Law irrelevant. Someone offered evidence based upon his own experience that mob programming breaks this rule.  I hope I’m not mischaracterizing his argument. That is,  I don’t think he’s saying that mob programming negates Brooks’ Law–I think he’s saying that multiple programmers can work faster via mob programming.

I had pointed out that while his experience is certainly evidence it’s anecdotal evidence.  This is not to belittle the importance of anecdotal evidence. It is to make a distinction between experimental evidence and anecdotal.  I’ll relate an example that I hope will help to clarify my thinking on this point.

I’ve raised domesticated pigeons for a good portion of my life.  I know lots of folks who race their pigeons (I do not).  I was told that one of the old sachems of pigeon breeding, a gentleman named Joe Quinn, was trying to convince breeders to vaccinate their birds.  He was having a hard time convincing the racers to vaccinate their birds and as a result, flocks were getting hit with easily preventable diseases.  So he hit upon an idea.  He convinced one of the better breeders to let him (Joe) vaccinate his flock at no charge.  The breeder didn’t mind since it was free.  At the end of the season the other fanciers asked the master breeder what was his secret of winning races.  The master breeder said that he did pretty much what everyone else did (that is, training, selective breeding etc.) but he did get his flock vaccinated.  The next season all of the fanciers wanted to get their flock vaccinated.

This is what I mean by anecdotal evidence. Did the vaccinations make a difference?  Of course they did–they protected the birds against preventable diseases. But did the vaccinations make a difference in the races?  That’s more debatable.

Experimental evidence would be vaccinating half of the flock and leaving the other half unvaccinated and then comparing how the birds raced at the end of the season.  Of course everything else would need to be held equal–equal diet, equal training, equal housing etc.  The result would be experimental evidence that the vaccination either made the birds race faster or not.

As it stands however, saying vaccinating the flock made the birds race faster is anecdotal evidence.  Heck we might even be able to prove it had no effect by looking at the race times pre-vaccination and post-vaccination–although there are likely to be other complicating factors present.

The thing is it’s really hard to do a double-blind study this way when it comes to software development because there are just too many other factors to try to hold equal.  If one team uses mob programming and they’re more productive–well was it the mob programming that made them more productive or was it that they had an easier task to accomplish or was it the fact that the team had long experience together or so forth and so on.

So we’re mostly stuck with anecdotal evidence when it comes to software development because producing experimental evidence is so difficult.  As I said on Twitter I’m not doubting the anecdotal evidence–I’m just doubting that mob programming is the sole difference.  The fact, all by itself,  that a team is willing to try mob programming probably indicates a team more receptive to trying new ideas and hence possibly more productive for that reason alone.  I’m not doubting that mob programming is beneficial; while I don’t have experimental evidence to prove it, I also believe it’s a good idea.

Grow-Only Set In C#

So I’ve been trying to keep my development skills somewhat sharp by working on coding up certain data structures in C#.  Course I’d rather do this with F# or even some more interesting FP language (Elm anyone?) but I do have a need to be able to show this code to student developers and the students I get know C#.

So there are a few things:

1.) I found it hard to find an abstract discussion of these CRDT data structures.  This article on Wikipedia is fine but there’s some math notation there that I find a bit tough to read. I suppose I need to do a bit more digging.

2.) It’s a bit of an impedance mismatch to try to build a read-only data structure in an imperative language.  OO especially doesn’t really seem to fit the idea of returning new data (as opposed to mutating in place) as well as it might.  Still we soldier on as best we can.

Without any further ado here’s my code.

using System.Collections.Generic;
using System;
using System.Linq;

namespace CRDT
{
    public class GrowOnlySet<T>
    {
        private readonly HashSet<T> payload;

        public GrowOnlySet()
        {
            payload = new HashSet<T>();
        }

        public GrowOnlySet(HashSet<T> newstore)
        {
            payload = newstore ?? throw new ArgumentNullException(nameof(newstore));
        }

        public HashSet<T> GetPayload() => new HashSet<T>(payload);

        public GrowOnlySet<T> Add(T element)
        {
            payload.Add(element);
            return new GrowOnlySet<T>(GetPayload());
        }

        public bool Lookup(T element) => payload.Contains(element);

        public bool Compare(GrowOnlySet<T> other) => (other == null) ? false : other.GetPayload().IsSubsetOf(payload);

        public GrowOnlySet<T> Merge(GrowOnlySet<T> other)
        {
            if(other == null) throw new ArgumentNullException(nameof(other));
            return new GrowOnlySet<T>(new HashSet<T>(payload.Union(other.GetPayload())));
        }
    }
}

I would greatly appreciate any comments that folks with a deeper knowledge of CRDT data structures may care to share.

Some Advice To The Young Lions

While I’ve done a lot of things that I now regret, I can say that I’ve been somewhat blessed to have learned from some of my mistakes.  So please believe that this advice comes from sad experience and that I’m just trying to help some folks avoid the career limiting moves that I have made over the years.

1.) There will be politics.  Unless you work in a one-man shop consisting of yourself and yourself alone there’s going to be politics. And to be clear when I say “politics” I mean decisions made not because of some valid technical justification but rather made because someone’s friend’s kid needs a job or because some leader still holds a grudge against another leader from 10 years ago. It’s a fact of life.  It’s annoying.  Do your best to do the best job you can and accept that even though it’s galling there will be lots of decisions made based not on technical merit but based on prejudices, biases and plain old ignorance.  If you’ve explained why one course of action has more technical merit than another and they still choose the less technically meritorious approach, you can look yourself in the mirror and know you’ve done what you can.

2.) Don’t dwell on the negatives. While it’s good to have your eyes wide open and see things as they truly are, if you’re constantly focused on the bad decisions and the arbitrary annoyances, you won’t change them but you will make yourself very unhappy.  Again, in any situation you work in there are going to be downsides.  If you focus on the downsides to the exclusion of everything else, you’ll just make yourself angry and you won’t accomplish very much at all.

3.) Don’t bifurcate the world.  As much as we wished the world were a nice definite composition of black and white, it isn’t.  Good people do bad things (all the time) and bad people sometimes do the right thing in spite of the fact they’re bad.  Life is ambiguous; deal with it.  Don’t be too quick to judge someone as good or bad or smart or dumb because people are contradictory creatures.

 

 

What Makes A Senior Developer

I’ve had a lot of occasion lately to think about what makes a developer a “senior” or the sort of developer who’s your go to for solving a tough coding problem.  Like so many things in life I can only define senior in terms of negatives–that is what is it that junior developers lack that seniors seem to have.  I can list some of them and I’d love to hear from others as well.

1.) Only knows one development language.  This is to my mind the hallmark of the beginner.  And please don’t misunderstand me; I’m not faulting juniors. We all have to start somewhere.  But if you only know one development language, no matter how well you know it, you’re not a senior. In fact I’d venture to say that seniors usually know at least three languages and they’re fluent in them:

A scripting language: e. g. python, ruby, bash etc.

A database language:  SQL but also some understanding of DB theory and DB structure.  That is, if you ask a senior about a primary key he or she won’t scratch his or her head and say “what’s that?”

The language they use for the day job: Java, C#, C++ etc.

2.) Knows nothing or next to nothing about data structures.  This is one that I expect people could make a strong argument against–most of us don’t need to roll our own linked lists or queues on a daily basis.  But there’s a large difference between not needing to write data structures and being totally ignorant of the concept.  I’ve never needed to write a doubly-linked list but I know what one is and I have a notion of how I would encode it.

I also realize that a lot of us are self-taught.  No matter.  I wouldn’t trust an engineer that didn’t know the properties of the materials he or she is designing with; I don’t trust a software developer who has no conception of data structures or their properties.

3.) Knows nothing or next to nothing about using a CLI.  Again given the advent of powerful IDEs I’m sure a lot of folks will disagree with me on this. So be it. CLIs are the developers’ power tools. If I want precise behavior and I want a repeatable build I want a CLI with a script (see above).  Plus when it comes to IDEs let’s be honest–if you’re not doing a mainstream language (and mostly C# or Java) you won’t have an IDE.  That leaves you out of 90% of the most interesting stuff going on in the realm of software development these days.

4.) Only goes to development conferences if someone else pays for it.  If you’re not so hellbent on learning new stuff about writing better software that you won’t pay to go to a conference then you’re simply not a senior.

I realize that there are people who don’t have the disposable income to attend conferences.  There are user groups in pretty much every major metropolitan area in the US and overseas as well–if you can’t pay to go to a conference then attend a local user group. Better yet present something interesting at the local user group–most of them are always looking for people to give their monthly talk. And if there’s not a user group that is discussing what you want to learn about then start one. The main thing is a strong, strong desire to keep improving your software development game.

These are just four of the qualities that I see lacking in many junior devs that want to become senior devs.  I’d love to hear what anyone else thinks on this subject as well.

 

 

NLOOVM

Just a sort of random thought; I think it’s interesting to see the number of efforts to build new languages on old vm’s (NLOOVM).  For example:

JVM:

  • Clojure
  • Scala
  • Kotlin

BEAM:

  • Elixir
  • Lisp Flavor Erlang (LFE)
  • Alpaca

These new languages are getting lots of traction.  Likewise it’s an interesting (and somewhat puzzling) situation with the CLR.  Pretty much anyone working on the CLR is working on C#.  Oh there are other languages on the CLR but they have nowhere near the level of interest or penetration of either Clojure or Elixir respectively.

Tooling Not Craft

For a while, I’ve had the nagging feeling that there’s something missing with the way that many junior developers learn to write software these days. In a way that I suppose makes me a bit of a curmudgeon, I somewhat long for the days before the prevalence of the IDE and the automated unit test framework.   But rationally I know that IDE’s and other specialized tooling have done a lot to both cut grunt work and to eliminate accidental complexity so it’s hard to deny they’re a big step forward from what came before.

And then it occurred to me.  Some of my issue with the modern tendency toward doing everything in the IDE is the tendency that weaker developers have to conflate tooling with craft.  Because Eclipse has the ability to automatically generate getters and setters for each data member of a Java class they simply stop questioning the wisdom of having getters and setters for all the members and merrily march forward–exposing far more data than they should.  The benefit of data hiding, which, after all, was the original benefit of private class members is largely lost because they can’t be bothered to actually think through the wisdom of effectively turning a class into a pseudo-struct.

A long time ago when I first began coding I had a senior developer who mentored me.  I had a bit of a tendency to charge in and start changing code (I am still guilty of that I suppose) without really thinking through the problem.  He wisely counseled me to take a few minutes to think about the problem before I started hacking.  Now I think I understand how he felt.

Don’t get me wrong–better tooling is definitely a good thing.  But don’t let your tooling dictate your craft. And if you want to be a truly better developer focus on learning the craft of building strong code.  The tooling is important, of course, but the underlying craft is much more important.

Some Updates On “LDAP Authentication With Phoenix”

There’s an excellent write-up on authenticating users against an LDAP database with Phoenix which was created by Richard Nyström.  For some reason even though it’s barely a year old, there seem to be a few points that are either out of date or simply incorrect. I worked through his example and I wanted to share a few amendments to his original code in the interests of other developers who may want to use LDAP with Phoenix.

First the set up of Phoenix


mix phx.new ldap_example # was mix phoenix.new ldap_example
...
Fetch and install dependencies? [Yn] Y

cd ldap_example

mix ecto.create (configure your db in config/dev.exs if needed)

mix phx.gen.schema User users username:string name:string email:string
# was
# mix phoenix.gen.model User users username:string name:string email:string

mix ecto.migrate

Next change is in the session_controller.ex file:


#was LdapExample.SessionController

defmodule LdapExampleWeb.SessionController do

#was LdapExample.Web, :controller

use LdapExampleWeb, :controller
alias LdapExample.{User, Repo, Ldap}

def new(conn, _params) do
render conn, "new.html", changeset: User.login_changeset
end

def create(conn, %{"user" => params}) do
username = params["username"]
password = params["password"]
case Ldap.authenticate(username, password) do
:ok -> handle_sign_in(conn, username)
_ -> handle_error(conn)
end
end

defp handle_sign_in(conn, username) do
{:ok, user} = insert_or_update_user(username)
conn
|> put_flash(:info, "Logged in.")
|> Guardian.Plug.sign_in(user)
|> redirect(to: page_path(conn, :index))
end

defp insert_or_update_user(username) do
{:ok, ldap_entry} = Ldap.get_by_uid(username)
user_attributes = Ldap.to_map(ldap_entry)
user = Repo.get_by(User, username: username)
changeset =
case user do
nil -> User.changeset(%User{}, user_attributes)
_ -> User.changeset(user, user_attributes)
end
Repo.insert_or_update changeset
end

defp handle_error(conn) do
conn
|> put_flash(:error, "Wrong username or password")
|> redirect(to: page_path(conn, :new))
end

def delete(conn, _params) do
Guardian.Plug.sign_out(conn)
|> put_flash(:info, "Logged out successfully.")
|> redirect(to: "/")
end
end

The changes to user.ex are not made in the web/model/user.ex; rather they’re made in the existing user.ex file.

And finally a change to session_view.ex

#was LdapExample.SessionView

defmodule LdapExampleWeb.SessionView do

use LdapExampleWeb, :view

end