Squads

This is an organizational pattern I like:

  • 2-5 ppl
  • Cross-functional
  • Focused on a specific goal
  • Weekly demo to squad

I’ve heard this refered to as a “squad”, “swarm”, “e team” and “feature team”.

One of the nice things is the sense of comraderie from working closely with a small group on a specific goal. Another nice thing is the group dissolves after the goal is accomplished, giving the members closure and chances to try new things without switching teams. Another benefit is broad awareness of how a system works.

This pattern works well in a larger context:

  • Shared code ownership
  • 10-50 ppl
  • Focused on a specific, but larger goal
  • Fortnightly demos from all squads
  • Shared calendar for all squad coordination meetings

When a squad accomplishes its goal, the members dissolve into the larger group. Individuals can learn about work in the larger group by attending the fortnightly demos and/or sitting in on other squads’ coordination meetings.

For example, a product may be supported by several teams. To avoid exposing the org chart in the product, make a large team who’s goal is to make that product excellent. Define a squad for each significant work item. All members of the large group are free to contribute code to the product.

An underlying principle is alignment around customer value over specific products or features. Rather than a group of people owning a code base in perpetuity, regardless of the amount of work required, squads form in response to need.

A counter-example would be several teams supporting a product and that product having disjoint features. Another counter-example would be a large team trying to maintain the interest of all its members in weekly coordination meetings. Yet another counter-example would be lots of individuals working in isolation, esp if they’re doing so to avoid coordination co

The Manager’s Path

I’m an individual contributor, but I want to better understand management’s concerns, so I’m reading Camille Fournier’s excellent The Manager’s Path. These are my notes.

Many think a neutral relationship with management is good because at least it’s not negative, but there is such a thing as a positive relationship w mgmt.

1-1 mtngs:

  • two purposes:
    • Human connection
    • Private conversation, eg feedback
  • I agree w the above two, and would add a third:
    • To ensure time w otherwise busy ppl; the junior person has the priority
  • Not for status
  • Prepare an agenda. I like a living doc, linked from the mtng invite. Items can be added and referenced any time
  • “Regular 1-1s are like oil changes; if you skip them, plan to get stranded …”
  • “Try to keep notes in a shared document” 👍 I like to link an agenda doc from the mtng invite. (Same for most recurring mtngs.)

As you become more senior, feedback decreases.

Appreciate the fact that current peers turn into future jobs.

Uncertainty
– common every 5-10 yrs
– lots of uncertainty in the world
– ultimately, we have to rely on ourselves

People aren’t good at saying what they mean in a way others can understand, so we have to listen carefully to words, and non-verbal cues indicating the person feels understood.

“Be prepared to say anything complex a few times and in diferent ways.” I’ve found such repetition frustrating in the past. It’s validating to see this advice.

Effective teams have good onboarding documents. Have new hires update the docs as their initial contribution.

“What you measure, you improve.”

Beware alpha-geek tendencies. In particular, the tendency to lecture and debate.

Mentorship skills:
– keep an open mind, since the mentee brings fresh eyes
– listen and speak their language. If you can’t hear the question being asked, you can’t provide good answers
– use the mentorship to build your network

“Tech lead is not a job for the person who wants the freedom to focus deeply on the details of her own code.”

“… the tech lead role may be held by many different stages of engineer, and may be passed from one engineer to another without either person necessarily changing his functional job level.”

“… we know from the title that it is expected to be both a technical position and a leadership role.” In other words, it’s not necessarily superlative, ie TL != best.

“The tech lead is learning to be a strong technical project manager… and [is] learning how to handle difficult management and leadership situations”

“Realistically, it is very hard to grow past senior engineer 2 without ever having acted as a tech lead, even on the individual contributor track… people skills are what we’re asking the new tech lead to stretch, more than pure technical expertise.” This stands out to me because of the tension between manager and maker modes, to use Paul Graham’s terminology.

“Being a tech lead is an exercise in influencing without authority …” Including building a psychological skill set for managing associated stresses.

“From now on … balancing is likely to be on of your core challenges.”

Currently, it feels like I’m working two jobs, manager mode during the day, and maker mode in morning and evenings. Regular, project-specific “cadence” meetings have helped reduce ad hoc discussions, fwiw.

Ah, a few lines later: “Some days you’re on maker’s schedule, and some days your on manager’s schedule…It’s very difficult to get into the groove of writing code if you’re interrupted every hour by a meeting.”

“Part of your leadership is helping the other stakeholders … respect the team’s focus and set up meeting calendars that are not overwhelming for individual contributors.” I’m very happy to see this in a book about managing thought workers.

Main roles of tech lead

  • Architect and business analyst. Design the system enough to provide estimates and ensure requirements are met
  • Project planner. The goal is to maximize parallelization
  • Developer and lead. Write code, but not too much. The goal is the project (and team development), not individual tasks

“Sometimes tech leads are tempted to go to heroics and push through obstacles themselves… [but] you should communicate the obstacle first.” I can relate with the former and appreciate the actionable latter.

“Teams often fail because they overworked themselves on a feature their product manager would have been willing to compromise on.” So, communicate.

“… most managers will expect their tech leads to continue writing as much code as before … It’s generally a pure increase in responsibility …”

The goal of a project plan is a “degree of forethought, in places where you can reasonable make predictions and plan … The plan itself … is less important than the act of planning.”

Take time to explain. No one who’s not actively working on a project should be expected to immediately know and understand project details.

Do a premortem as part of project planning. How could the system fail, and what could we do to recover?

“Having the focus to build something big yourself is a distant memory.”

The agile principles can be a healthy alternative to rigid process 👍 I think they’re great.

“… no two great teams ever look exactly alike in process, tools or work style” The best thing I’ve seen is an appreciation of experimentation and iteratively building a style that works for the current team. A basic project plan, ie list of tasks, also seems like a universal business requirement. Put another way, revisiting that plan periodically seems like a reasonable, universal starting point.

Qualities of a great tech lead:

  • Understand the architecture
  • Help build, but involve others
  • Lead decisions, but do so collaboratively
  • Communicate

“You want to encourage others on your team to learn the entire system … but you don’t always need to be self-sacrificing” There’s the need for a sense of balance again.

“Your productivity is now less important than the productivity of the whole team.” But how to improve the productivity of the team without putting on a management hat? Fournier gives an example: “Represent the team in meetings.”

Possession of communication skills differentiates successful leaders.

“Practice repeating things back to people to ensure you understand them.” I like this! I think it pairs well w advice earlier in the book to listen and observe non-verbal cues.

Communicate and listen.

I’d add that the tech lead label can also make one a focal point for questions, eg support, which can disrupt focus work. I like the pattern of having a support rotation, but depending on the company, the convention may be to simply ping the TL.

“Respect the ‘maker schedule’ for reports” 👍 As a general rule, I appreciate biasing toward contiguous meeting blocks.

Autonomy … is an important element of motivation.” I see this w external contributions too. Maximizing an integrating team’s autonomy frees them to meet their goals w minimal bottlenecks.

Notes from “Web Application Development with Closure Compiler” talk by Alan Leung on 6/22/11

Alan visited Twitter on 6/22 and presented an introductory talk on Google’s Closure compiler for JavaScript. Alan is tech lead on Closure team.

Here are the slides:
http://acleung.com/twitter_talk.pdf

Notes

* JavaScript was originally designed for small DOM operations. Now that we’re building large-scale apps in JS, we can use some help.
* Google uses Closure for all but a couple products
* The Closure compiler can perform ~55 optimization passes, including linting code, validating function definitions, performing gzip-optimized compression, trimming dead branches
* Closure can also provide compile-time constants, e.g., “if(INTERNAL){…”, and trim unused branches that result
* Closure uses a graph coloring heuristic for variable renaming

 

hello world with Scala and sbt

Preamble

I’d like to learn more about Scala.  To get started, I’m going to run through a hello world example on sbt’s Google Code site.  Here are my system details:

  • Mac 10.6.6
  • Scala 2.7.7
  • sbt 0.7.4
  • Java 1.6

Steps

  1. Create a new sbt project by simply running the command sbt.  I set the name to “hw”, the organization to “foo.com”, and the project version to “1.0”, and accepted the defaults for the Scala and sbt versions but just hitting enter.  After building the project, sbt will leave a command prompt open.  Close this by typing ctrl-D.  Note: The quick start creates the code first, but then doesn’t talk about where to put it, which I found a bit confusing, so I’m presenting it here in reverse order.

    Screen shot of sbt creating a project
    Screen shot of sbt creating a project
  2. Create the file hw.scala in the src/main/scala/ directory generated by sbt:
    echo ‘object Hi { def main(args: Array[String]) { println(“Hi!”) } }’ > src/main/scala/hw.scala
  3. Compile the project: sbt compile

    Screen shot of compiling a project
    Screen shot of compiling a project
  4. Run the project: sbt run

    Screen shot of running a project
    Screen shot of running a project

Conclusion

So that’s that. It’s just hello world, but I found it helpful as an introduction to sbt’s relationship to Scala.  Time to call it a day, like these birds are doing:

birds on a wire
"birds on a wire" by touterse

YUI 2in3 Modal Panel Example

Suppose you’re using YUI 3.2 and you’d like a modal dialog.  YUI 3 Overlay provides an easy way to position an element above the others, but it doesn’t provide modality.  The Overlay Extras gallery module sounds perfect, but it seems to work best with YUI 3.1.0.

YUI 2 has exactly what we’re looking for in its Panel widget.  Fortunately, the YUI 2in3 project makes the Panel available in YUI 3.2.  

The sample code below demonstrates usage, and you can see a demo on
demo-deluxe.heroku.com/static/1/index.html.

    <body class="yui-skin-sam">
            
        <p><button id="show">show modal</button></p>
        
        <!-- modal dialog content markup -->
        <div id="content" style="visibility:hidden">
            <div class="hd">Header</div>
            <div class="bd">
                Body
                <p><button id="hide">hide modal</button></p>
            </div>
            <div class="ft">Footer</div>
        </div>
        
        <script src="http://yui.yahooapis.com/3.2.0/build/yui/yui-min.js"></script>
        <script>
        YUI().use('yui2-container', 'yui2-dragdrop', 'event', function(Y) {

            var YAHOO = Y.YUI2;
            
            var modal = new YAHOO.widget.Panel("content", {
                width: "240px",
                fixedcenter: true,
                close: true,
                draggable: true,
                zindex: 4,
                modal: true,
                visible: false
            });
            modal.render(document.body);

            Y.one('#show').on('click', function() {
                modal.show();
            });
            Y.one('#hide').on('click', function() {
                modal.hide();
            });

        });
        </script>
        
    </body>

 

getting started with ufw on Ubuntu server

Rather than read the entrails of iptables syntax, I’d prefer to continue respecting myself, and use something more user-friendly, something like the Uncomplicated Firewall (ufw). I just learned about this, but it’s the Ubuntu default. I’d like to have faith, but for now I just hope it doesn’t suck.

The UFW wiki page linked above states “Setting the default mode of ufw is recommended before turning it on …”: sudo ufw default deny

That was easy. Next, I’ll enable it: sudo ufw enable

Now, I’ll allow port 2222 for ssh: sudo ufw allow 2222

And allow port 80 for tcp: sudo ufw allow 80/tcp

Check the current settings: sudo ufw status

Turn on logging: sudo ufw logging on

Ubuntu’s saying I need to restart in order for the changes to take effect. Fingers-crossed. Holding breath: sudo shutdown -r now …

As an aside, why does it have to be this way? Why can’t we just know that it will work? To be fair, UFW does seem pretty simple, and UFW’s –dry-run might be exactly what I’m looking for. Hopefully, UFW keeps me safe. Once I figure out how to automate deployment, things might be ok.

Ok, let’s check on the our ability to log in locally …

Good. I can still log in locally, so I didn’t lock myself out utterly. Checking local ssh as a sanity check: ssh -v localhost -p 2222

That works. Checking remote log in: ssh erik@172.16.83.133 -p 2222

Hmm. That still doesn’t work, and nothing is showing up in either /var/logs/auth.log or /var/logs/messages. Lemme try viewing the iptables directly:
sudo iptables -L

Wow. UFW knows how to generate iptables. The list goes on forever. Let’s try again w/ less: sudo iptables -L | less

I can see 2222, www, and ssh allowed, so it’s not obvious why I can’t ssh in. Following a couple suggestions on the vmware forums, I set /etc/hosts.allow to “SSHD:ALL”, and set networking to “bridged”, but still no luck.

Ok. I’ll hang it up for now. Here’s a nice, soothing picture of a kelp forrest to chill out to:

Kelp Forrest at Monterey Bay Aquarium
Photo credit: Moral Threat

First experiences with Rack::Test

I love test-driven development, and I love Rack apps, so I was delighted to discover the Rack::Test toolset.  But, I wasn’t able to get it working immediately using the documentation I could find, so I’m taking notes here along my journey to discovery.

I. Install Rack::Test

The docs on the Rack::Test site didn’t do me wrong.  Rack::Test installed cleanly with: 
sudo gem install rack-test

II. Define some test code to get started

I grabbed the sample code from the Rack::Test site and saved it into a file called test.rb.

  require "rack/test"

  class HomepageTest < Test::Unit::TestCase
    include Rack::Test::Methods

    def app
      MyApp.new
    end

    def test_redirect_logged_in_users_to_dashboard
      authorize "bryan", "secret"
      get "/"
      follow_redirect!

      assert_equal "http://example.org/redirected", last_request.url
      assert last_response.ok?
    end

  end

III. Run the code

Now, this is where I stumbled. How do we run this?

I tried rackup test.rb and ruby test.rb, but both complained of an “uninitialized constant Test”, so I guess there’s a prerequisite.

I checked out the Rack::Test Gemfile and installed rspec and upgraded rack to no avail.

I’m on Mac 10.6, btw.  I’ve got rack 1.2.1 and rack-test 0.5.6.

Aha!  As per a stackoverflow thread, I learned I need to add require “test/unit” to my code, so it looks like

require "rack/test"
require "test/unit"
...

Now, when I run ruby test.rb, it throws, `require’: no such file to load — rack/test, but this is easily solved by requiring rubygems:

require "rubygems"
require "rack/test"
require "test/unit"
...

Dah! NameError: uninitialized constant HomepageTest::MyApp

I’ll just use Sinatra, that’s my end goal anyways.

require "rubygems"
require "sinatra"
require "rack/test"
require "test/unit"

class HomepageTest < Test::Unit::TestCase
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end
...

There we go:

Loaded suite test.rb
Started
E
Finished in 0.007586 seconds.

1) Error:
test_redirect_logged_in_users_to_dashboard(HomepageTest):
Rack::Test::Error: Last response was not a redirect. Cannot follow_redirect!

Now it’s time for bed.  Sweet dreams

Leaning Palm HDR, "A stretch of beach along the Blue Lagoon on the atoll of Rangiroa."
Credit: vgm8383

Helpful links

Debugging OAuth

This is some code I use to debug oauth issues.
Note: there is a bug somewhere in this that results in an invalid signature.  Please let me know if you spot it.
Usage:
  1. Upload this file to your server
  2. Get a key/secret from your oauth data provider.  The code currently has Yahoo! hardcoded as the provider, so just change the endpoints to use another one.

<?php // a php script that uses the standard oauth lib (via yos sdk) to do the oauth dance
// error_reporting(E_ALL);
require '../yosdk/yahoo-yos-social-php5-86eef28/lib/OAuth/OAuth.php';

// we've got a stored access token
if ( $_COOKIE['serialized_access_token'] ) {

    $access_token = json_decode( stripslashes( $_COOKIE['serialized_access_token'] ) );

    printf('<pre>%s</pre>', print_r($access_token, true));

// we're on the callback
} elseif ( $_COOKIE['serialized_request_token'] && $_GET['oauth_verifier'] ) {

    //debug - sanity check to see if input is passed correctly
    // echo $_GET['oauth_verifier'].$_COOKIE['callback'];die;

    $consumer = new OAuthConsumer($_COOKIE['key'], $_COOKIE['secret']);

    $parameters = array('oauth_verifier' => $_GET['oauth_verifier'], 'oauth_callback' => $_COOKIE['callback']);

    $request_token = json_decode( stripslashes( $_COOKIE['serialized_request_token'] ) );

    //debug - make sure the request token decoded properly
    // var_dump($request_token); die;

    //kludge
    $request_token->key = $_GET['oauth_token'];

    $request = OAuthRequest::from_consumer_and_token(
        $consumer, $request_token, 'GET', 'https://api.login.yahoo.com/oauth/v2/get_token', $parameters);

    //debug - see params: useful for debugging empty variable issues
    // var_dump($request); die;

    $request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(), $consumer, $request_token);

    //debug - see base string: useful for debugging encoding issues
    var_dump($request); die;

    //debug - see url: useful for sanity checking actual request to server
    // echo $request->to_url(); die;

    $curl = curl_init($request->to_url());
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HEADER, true);
    $response = curl_exec($curl);

    //debug - see raw response, incl headers, which can contain additional info
    // var_dump($response); die;

    curl_close($curl);

    parse_str($response, $access_token);

    //debug - see parsed data: useful for debugging parsing bugs
    // var_dump($token); die;

    // clear req token
    setcookie('serialized_request_token', '', time()-3600);

    // cache access token
    setcookie('serialized_access_token', json_encode( $access_token ) );

    printf('<pre>%s</pre>', print_r($access_token, true));

    exit;

// we just submitted the form
} elseif( $_GET['submit'] ){

    $consumer = new OAuthConsumer($_GET['key'], $_GET['secret']);

    $parameters = array('oauth_callback' => $_GET['callback']);
    $request = OAuthRequest::from_consumer_and_token($consumer, null, 'GET', 'https://api.login.yahoo.com/oauth/v2/get_request_token', $parameters);

    $request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(), $consumer, null);

    //debug - see base string: useful for debugging encoding issues
    // var_dump($request); die;

    //debug - see url: useful for sanity checking actual request to server
    // echo $request->to_url(); die;

    $curl = curl_init($request->to_url());
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($curl);
    curl_close($curl);

    //debug - see raw response, incl headers, which can contain additional info
    // var_dump($response); die;

    parse_str($response, $token);

    //debug - see parsed data: useful for debugging parsing bugs
    // var_dump($token); die;

    // cache params & token for 2nd step
    setcookie('key', $_GET['key'] );
    setcookie('secret', $_GET['secret'] );
    setcookie('callback', $_GET['callback'] );
    setcookie('serialized_request_token', json_encode($token));

    $params = array('oauth_token'=>$token['oauth_token']);
    header("Location: https://api.login.yahoo.com/oauth/v2/request_auth?".http_build_query($params));
    exit;
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.8.1/build/reset-fonts-grids/reset-fonts-grids.css">
        <style>
            body {
                padding: 20px;
            }
            button {
                float: left;
                background-color: #fff;
                padding: 1ex;
                margin: 2ex 0;
            }
            label {
                display: block;
                text-align: left;
                width: 10em;
            }
            input {
                float: left;
                width: 64em;
                padding: 1ex;
                margin: 2ex 0;
            }
            #submit {
                width: 7em;
            }
        </style>
    </head>
    <body>
        <form>
            <div>
                <label>Consumer key:</label><input name="key" value="">
                <div style="clear:both"/>
            </div>
            <div>
                <label>Consumer secret:</label><input name="secret" value="">
                <div style="clear:both"/>
            </div>
            <div>
                <label>Callback URL:</label><input name="callback" value="">
                <div style="clear:both"/>
            </div>
            <input value="Authorize" name="submit" type="submit" id="submit">
        </form>
    </body>
</html>

centos 5 yum update error & resolution

I just tried to update my centos 5 install via yum and got the following error messages:

filelists.sqlite.bz2                                                                                                                                                                 | 1.5 MB     00:01     
http://centos.eecs.wsu.edu/5.4/updates/i386/repodata/filelists.sqlite.bz2: [Errno -1] Metadata file does not match checksum
Trying other mirror.
filelists.sqlite.bz2                                                                                                                                                                 | 1.1 MB     00:00     
http://mirror.facebook.net/centos/5.4/updates/i386/repodata/filelists.sqlite.bz2: [Errno -1] Metadata file does not match checksum
Trying other mirror.
....

I searched on line for “yum update Metadata file does not match checksum” and found a helpful blog post. Following the post suggestion, I ran yum clean all, which seems to have fixed the problem.