Software Engineering

September 25, 2014

Table of contents

  1. Getting things done
  2. Release planing
  3. Maintaining maintainability
  4. Prioritizing tasks
  5. Test-driven development
  6. Access control
  7. Progressive enhancement

Getting things done

  1. Decide what to do
  2. Do it

Remember to do the first step.

Release planning

  1. Budget
  2. Features
  3. Deadline

Choose two.

Maintaining maintainability

With the exception of MIDI 1.0, software development never seems to reach a satisfactory point of completion.

One of the lessons of The Mythical Man Month is to plan to throw (the first) one away. Conway's Law and microservices architectures carry this idea to the extreme by encouraging the development of services that are so simple as to be disposable.

Maintenance is expensive. Embracing this inevitability frees us from the burdens of API evolution, feature deprecation, bit rot, regressions, and the endless frameworks du jour.

Prioritizing tasks

Establish a consistent measurement scale for cost. It can be quantitative or qualitative. Do the same for a measurement scale for benefit.

Create a cost vs. benefit graph, and add all of your tasks to it:

                  ^
      World peace |
                  |   foo
                  |                                  raz
                  |                     bar
BENEFIT           |                                            qux
                  |
                  |
                  |             baz
    Strong coffee |
                  `---------------------------------------------------->
                   Tip the barista                          Scientology
                                           COST

Prioritization can now be determined by a function of cost and benefit. Pick an initial prioritization schedule, such as y = x:

                  ^                                                 ┐
      World peace |                                            .
                  |   foo                                .
                  |                                  raz
                  |                     bar     .
BENEFIT           |                        .                   qux
                  |                   .
                  |              .
                  |         .   baz
    Strong coffee |    .
                  `---------------------------------------------------->
                   Tip the barista                          Scientology
                                           COST

The benefit of any task above the line outweighs its cost, so it should be prioritized. The cost of any task below the line outweights its benefit, so it should be deprioritized.

Here, we see that foo and bar are clearly worth their costs, raz is debatable, and quz and baz are not worth their costs.

Perhaps cost is more expensive, relative to benefit, than as captured above. This might be better represented by a prioritization schedule of y = 5x:

                  ^         ┐
      World peace |        .
                  |   foo .
                  |      .                           raz
                  |     .               bar
BENEFIT           |    .                                       qux
                  |   .
                  |  .
                  | .           baz
    Strong coffee |.
                  `---------------------------------------------------->
                   Tip the barista                          Scientology
                                           COST

Now only foo is worth its cost. Everything else should be deprioritized.

Experiment with different prioritization schedules. Continuously reevaluate the costs and benefits of tasks as new information becomes available and as business needs change.

Test-driven development

  1. Create a new branch for the new feature

    $ git checkout -b myfeature
    $ git push origin myfeature
  2. Create a new branch for test development

    $ git checkout -b myfeature/test
  3. Write (failing) test code that encapsulates the intent of the new feature

  4. Push the test code to the myfeature/test branch

    $ git add ...
    $ git commit ...
    $ git push origin myfeature/test
  5. Open a pull request from myfeature/test to myfeature

  6. Create a new branch for feature development

    $ git checkout -b myfeature/impl
  7. Code up the feature to make the test pass

  8. Push the feature code to the myfeature/impl branch

    $ git add ...
    $ git commit ...
    $ git push origin myfeature/impl
  9. Open a pull request from myfeature/impl to myfeature

  10. Finally, open a pull request from myfeature to master

Access control

Strategies

  1. Tagged access control
  2. Context-based access control
  3. Context-based tagged access control

Tagged access control

Tagged access control is an approach to decoupling authorization logic from data models used to represent users and resources. This makes it trivially easy to support both changes and additions to user and resource data models over time.

Given a user, some actions, and a resource:

 O                    .
-+-      <access>     ├── dir1
 |                    |   └── file1
/ \      <modify>     └── file2

User      Actions     Resources

The user is granted access to perform certain actions for certain tags:

          access
 O  -----------------> tagX, tagY
-+- 
 |        modify
/ \ -----------------> tagY

User      Actions      Tags

The resource is marked with a tag:

.
├── dir1      ---------> tagX
|   └── file1 ---------> tagX
└── file2     ---------> tagY

Resource                 Tag

The user may perform an action on a resource if they have access to the resource's tag:

Context-based access control

Context-based access control addresses the need to limit access levels based on domain-specific constraints, such as the audience present, the networks utilized, the location occupied, etc.

Given two users, two resources, and access levels:

  O          .                  O
 -+- ------> ├── file1         -+-
  |  ------> ├── file2 <------  |
 / \         └── file3 <------ / \

User1        Resources        User2

If User1 and User2 are having a conversation, they are limited to discussing file2, since the intersection of their accesses excludes file1 and file3.

This also works for grantees that aren't users:

  O          .                 .~~~~~~.
 -+- ------> ├── file1         ;      ;
  |  ------> ├── file2 <------ ;      ;
 / \         └── file3 <------ '~~~~~~'

User1        Resources        Office LAN

If User1 needs to access file2, it must be done from Office LAN, since only the intersection of their accesses includes file2.

Progressive enhancement

JavaScript should never be required by basic features such as HTML forms.

Make it work without JavaScript

First, write basic functioning HTML.

<form action="" method="get">
  <input name="foo" type="text" size="24" placeholder="Type something" />
  <input type="submit" value="submit" />
</form>

Make it hip

After the basic HTML is working, add whatever unnecessary enhancements are desired. Use JavaScript code that, if it fails to run on clients that don't want/need it, won't break the underlying functionality.

<script language="javascript">
  var forms = document.getElementsByTagName('form');
  var form = forms[forms.length - 1];
  form.onsubmit =
    function() {
      showUnnecessarySpinner();
      return true;
    };
</script>

Try it out

Try submitting this form with JavaScript either enabled or disabled. Note that it works in both cases.