Posts from  September 2013


9.25.2013

Usability of Click To X emailed links

It's pretty common to get emails with links like the following:

  • Your package shipped, click to track
  • So and so sent you a friend request, click to accept
  • Your order is complete, click to view

I'm usually annoyed when I click the link and have to login, especially on mobile devices.

Frustration: the information usually isn't that sensitive.

It's beyond me why LinkedIn requires me to login to accept a connection.

Frustration: the sensitive information is already in the email.

Amazon, the tracking number is in the email, why do I have to login to view the tracking information via Amazon's system? This probably is a problem in about every system that sends tracking information.

If you inline the details of my order in the email, why require me to login to get the fancy PDF?

If someone compromised my email they can reset my password. Some could argue I'd know my password was reset, might not matter if the information is that important.

Frustration: I only have to login on a mobile device.

If I don't have to login on a desktop, make sure the same happens for mobile.

Make sure redirects to a "mobile friendly" version of the site don't pooch my ability to follow through on a mobile device (LinkedIn...)

Recommendation: One-time passwords

Many of these links include a random identifier and are prefixed with https, not http. I like to think of these as one-time passwords to access my account. If it makes you feel more secure, only allow access to the part of the account pertinent to the link.

Consider expiring links once the action is executed (accepting a friend request).

Consider expiring links after a time period if the information is sensitive (bank statements).

Why the fuss?

Because these emails are clogging the inboxes of the world, waiting for us to get back to, like we don't have enough of those already :). I'm of the mentality Do it or Defer it. The easier it is to Do the more likely I'll Do it instead of going to the trouble to Defer it.

Possible objections

  • "But after you add a friend, the system may want you to write on their wall or do some other thing?"
    • No problem, the link is basically a one-time password to access my account, have the link open the site to do that.
    • Or, prompt me the next time I login to the site.
  • "What about bank accounts"
    • Banks usually require more than email to reset a password, so I'd suggest either an expiring link or logging in, I'm usually not too annoyed by authenticating when accessing my bank account.
  • "What if someone else gets the link"
    • How did that happen? Probably a bigger problem than accepting my friend requests.
    • If the link is over https, if someone man in the middles that, they can man in the middle your username and password too.
    • No one is shoulder surfing a GUID.
  • Others? Please send them, I'd love to consider how I may be blatantly missing something of importance about this topic :)

Raised objections

  • "What about services that follow links to make sure they aren't viruses?" Ryan Ohs
    • Good point, in these cases if the link is an action I'd recommend having an "Accept" (fried requests), or some sort of "Are you sure". Probably a good idea in the event someone accidentally opens the link too.
    • And of course only expire the link after the action is executed

TLDR

Seriously consider sending links that don't require logging in, when it makes sense, it'll make everyone's life easier.

9.24.2013

Map Then Collect

Iterating through a collection of data and mapping to a new type is a rather routine development task. Here are various ways to do this. At the bottom I have a list of things to consider. What conclusions do you come to?

Note: in LINQ the map operator is named Select

Mapping

for Loop

public List<Output> ForLoop()
{
    var outputs = new List<Output>();
    var inputs = GetInputs();
    for (var i = 0; i < inputs.Count(); i++)
    {
        var input = inputs[i];
        var output = new Output();
        output.Id = Convert.ToInt32(input.Id);
        outputs.Add(output);
    }
    return outputs;
}

foreach Loop

public List<Output> ForEachLoop()
{
    var outputs = new List<Output>();
    foreach (var input in GetInputs())
    {
        var output = new Output();
        output.Id = Convert.ToInt32(input.Id);
        outputs.Add(output);
    }
    return outputs;
}

Functional ForEach

public List<Output> FunctionalForEachLoop()
{
    var outputs = new List<Output>();
    GetInputs()
        .ForEach(input => MapAndAdd(input, outputs));
    return outputs;
}

private void MapAndAdd(Input input, List<Output> outputs)
{
    var output = new Output();
    output.Id = Convert.ToInt32(input.Id);
    outputs.Add(output);
}

Functional Map Then Collect

public List<Output> FunctionalMapThenCollect()
{
    var outputs = GetInputs()
        .Select(input =>
        {
            var output = new Output();
            output.Id = Convert.ToInt32(input.Id);
            return output;
        })
        .ToList();
    return outputs;
}

Functional Constructor Map Then Collect

public List<Output> FunctionalConstructorMapThenCollect()
{
    var outputs = GetInputs()
        .Select(input => new Output(input))
        .ToList();
    return outputs;
}

Adding Filtering

foreach With Filter

public List<Output> ForEachWithFilter()
{
    var outputs = new List<Output>();
    foreach (var input in GetInputs())
    {
        int id;
        if (!Int32.TryParse(input.Id, out id))
        {
            continue;
        }

        var output = new Output();
        output.Id = id;
        outputs.Add(output);
    }
    return outputs;
}

Functional Map Then Filter Then Collect

public List<Output> FunctionalMapThenFilterThenCollect()
{
    var outputs = GetInputs()
        .Where(InputHasValidId)
        .Select(input => new Output(input))
        .ToList();
    return outputs;
}

private bool InputHasValidId(Input input)
{
    int id;
    return Int32.TryParse(input.Id, out id);
}

Considerations

  • Readable
  • Understanding / Confusing
  • Composition
  • Extensible
  • Intent
  • What is explicit?
  • What is implicit?

For reference

public class Input
{
    public string Id { get; set; }
}

public class Output
{
    public Output()
    {
    }

    public Output(Input input)
    {
        Id = Convert.ToInt32(input.Id);
    }

    public int Id { get; set; }
}

9.13.2013

Encapsulated Request Pattern

Many times we need to make requests to external systems when integrating applications. I've noticed a pattern I like when making these requests that simplifies understanding what all is involved.

Procedural submission

It's easy to end up with procedural code to execute requests, something like:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = client.Submit(request)
    if(response.Error) throw new Exception(...)
// continue with our code

Right in the middle of our application code, we conditionally decide if we need to submit a request, then we build, send and validate it all inline in our application code. Sometimes we make this one step better by extracting functions to deal with the request in some static class or injected service. This is compounded when we have multiple request types and we start to try to re-use parts of the process and end up with conditionals bleeding into the consumer code:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = SubmitRequest(client, request)
    if(response.Error) throw new Exception(...)
// continue with our code

// SubmitRequest(client, request)
    if(request is ThisTypeOfRequest)
        return SubmitThisTypeOfRequest(client, request);
    if(request is ThatTypeOfRequest)
        return SubmitThatTypeOfRequest(client, request);

// SubmitThisTypeOfRequest(client, request)
    // special code for This Type
    return client.Submit(request);

// SubmitThatTypeOfRequest(client, request)
    // special code for That Type
    return client.Submit(request);

This can get pretty hard to manage, and in some cases validating the response becomes conditional too and often this happens:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = SubmitRequest(client, request)
    if(response is ThisTypeOfResponse && response.Error) throw new Exception(...)
    if(response is ThatTypeOfResponse && response.InvalidWhatever) throw new InvalidException(...)
// continue with our code

Sometimes that response handling code at least ends up in the Submit methods that are extracted, either way it's pretty hard to follow as the parts of each request (building, submitting, validating) are scattered throughout some highly procedural code. It becomes hard to follow and prone to duplication and mistakes :(.

Encapsulated requests

One way I've found to help make things more apparent is to create a type per request. Like an anti-corruption layer around the request. In here I'll build, submit and validate each request:

// detect condition to send remote request
    request = new ThisTypeOfRequest()
    request.Submit()
// continue with our code

or

// detect condition to send remote request
    request = new ThatTypeOfRequest()
    request.Submit()
// continue with our code

class Request
    function Submit()
        request = Build()
        client = CreateClient()
        response = client.Submit(request)
        Validate(response)

class ThisTypeOfRequest extends Request
    function Build()
        // special code for This Type
    function Validate(response)
        if(response.Error) throw new Exception(...)

class ThatTypeOfRequest extends Request
    function Build()
        // special code for That Type
    function Validate(response)
        if(response.InvalidWhatever) throw new InvalidException(...)

Now, everything involved with a single request is in one class, in one file, one place to see what is going on! And in some cases, things like Submit can be shared across request types, and if it varies, it can be overridden. This pattern brings readability and re-use to the solution. Now consumers don't have to manually compose building, submitting and validating requests, they can focus simply on which requests to execute.

Steps to refactor to Encapsulated requests

If you have procedural code like this, try these steps to gradually refactor to this pattern:

  • Extract conditional building code into a single Build method that all consumers use
    • Then, in time, remove the conditionals and move the Building code for each request type into it's own class
  • Do the same for conditional submission code
  • Do the same for conditional validation code
  • Have patience to refactor in this direction over time, don't worry about doing this all at once.

Composing requests

Sometimes an event in our system triggers a need for multiple requests, almost as if the composite request doesn't exist in the external system. It becomes trivial to compose requests with this pattern:

class CompositeRequest extends Request
    function Build()
        _ThisRequest = new ThisTypeOfRequest()
        _ThatRequest = new ThatTypeOfRequest()
    override Submit()
        _ThisRequest.Submit()
        _ThatRequest.Submit()
    function Validate(response)
        // maybe nothing to do here

Now we can easily compose, with conditionals, multiple requests! Imagine if consumers had to compose this every time they needed this series of requests!

Analysis of pattern

  • Pros
    • Readability, one stop spot to see what all is involved with a request
    • Re-use
    • Avoids mistakes when consumers don't ensure all steps are executed
    • Composable
  • Cons
    • Time to refactor in this direction
    • Getting crazy with inheritance re-use. I'd recommend using a common interface (Build/Submit/Validate) but beyond that use composition.