Disable Atom.js Startup Welcome Guide


I've been test driving Atom.js and I like it pretty well. Something I don't like about it, is that the startup welcome guide keeps coming back. After several days of using it, I finally noticed how to turn it off.

In the left hand pane, at the bottom of the text there is a checkbox that disables the Welcome Guide. I think it's pretty hard to see, at least with the dark theme, but just uncheck it and you are good to go.

Managing By Whiteboard


Team lead is a different job than developer and requires a slightly different skillset.

While developers need to get along with others and coordinate their work, a team lead needs to coordinate lots of people's work - developers, stakeholders, users and their own. A team lead also needs to encourage and enable others to coordinate amongst themselves.

One simple technique is using the whiteboard as a tool to provoke discussion, make sure everyone is on the same page and to teach without appearing to teach.

The short explanation is that when a team member is explaining something complicated, asking a question or is making a decision as part of a group, stand up a start drawing what they say. Any mistakes will quickly be pointed out and everyone is working off the same information.

Whiteboarding to Learn

Credit jemimus @ Flickr

Often one of my teammates will have an idea that I don't understand. Or we'll want to do something neither of us understands.

To the whiteboard!

The process I typically follow is a teammate begins explaining something complex in a meeting. As soon as they summarize the concept that I don't understand, or suspect others don't understand, I'll politely ask if I can draw what I think they meant. I make it clear that I want to check my understanding. I then make my best attempt to draw what they just described.

Typically during drawing the team will have suggestions about naming things, or where to put lines. I listen to them all and draw what the collective will wants.

Whiteboarding to Teach

Teaching with a whiteboard

Credit juhansonin @ Flickr

Everyone has had to explain a concept to their team or a member of their team. You can try email, or talking, but for fast, quality results:

To the whiteboard!

This technique works best with advanced preparation, or at least a deep understanding of the topic. Starting with an overall block and arrow drawing to discuss and the drawing concepts as it becomes clear more explanation is needed is the basic outline of what to do.

A key technique, when teaching with the whiteboard, is to not monopolize drawing. The teacher should draw the initial concepts, but after that is done they should pass the marker to the student and encourage them to draw what they understand.

This has the added benefit that the teacher can see what they are not explaining well.

Whiteboarding to Communicate

Communicating with a whiteboard

Credit jm3 @ Flickr

Meetings are a vital part of a team lead's role. Meetings with stakeholders, meetings with supervisors, meetings with peers, meetings with team members, meetings with executives, even meetings with the public.

Meetings!

In many cases, Powerpoint is overkill or actively harmful.

To the whiteboard!

Whiteboards are light, adaptable and can be pre-prepared for your presentation. They also encourage audience participation, which enhances retention of information.

Whiteboards Everywhere

Use whiteboards as a tool to learn, to teach and to communicate. Don't just draw on them. Actively using a whiteboard and passing it around between participants ticks off two different learning style boxes - auditory and kinesthetic. The more learning styles you use, the more likely information is retained.

And remember - whiteboards can be transitory, but if you need to retain what you've drawn, take a picture.

Whiteboard everyday!

Problem Behavior Correction Flowchart


A few months back I took the Coast Guard Senior Leadership Principles and Skills class, which combines The Leadership Challenge with some Coast Guard specifics. I enjoyed the class a great deal - the instructors (Charlie Coiro and Cdr Scott Jones) were outstanding.

One resource they provided is the technique the Coast Guard uses to address problem behavior with its staff. This was something of a revelation for me, because while I had some vague sense of how to encourage people having problems, I lacked a structured way of intervening.

The technique the Coast Guard uses provides a structure that I like, because it separates the problem behavior from the employee themself. The trainers say that problems are seldom intentional and usually result from a misunderstanding or shortcoming of ability.

This matches my experience - essentially everybody I work with is a dedicated professional working toward the same goal, and problems arise not because of willingness but out of confusion.

Behavior Correction Flowchart, click for bigger

This chart is supposed to guide how a leader responds to a problem with one of their charges. The leader is supposed to correct problem behaviors by stating the standard vs the behavior, listening and then diagnosing why there is problem behavior. Depending on the underlying reason for the problem behavior, different approaches are advised.

The intent of this methodology is to emphasize that most problem behaviors are the result of misunderstanding, circumstance or under training and not any hostile intention. It further emphasizes not applying the flow chart beyond the point where the problem behavior ceases. It separates the behavior from the employee.

The example they gave at the training is a chronically late employee.

Example Dialog

Supervisor: I see you came in at 9:30 today. Our doors open at 9, and we expect staff to be here by then.

Role Clarity Example

Employee: I thought we had core hours between 10:00 and 2:00 and that if I were here during them and work the full 8 hours my starting/stopping time didn't matter?

Supervisor: That's an option at some offices, but we haven't started that program here. The customer service component of our job requires that we're all here to answer the phone at 9 promptly.

-or-

Supervisor: You're right, my mistake

Ability Example

Employee: I know, but I have such a hard time getting up. I've tried alarms, light timers and I just don't know what to do.

Supervisor: -Brainstorm ways to wake up here-

Supervisor: Try some new methods of getting up on time. It's important to be here at 9, so let's check in again in a month and see how things are working out

Willingness Example

Employee: I get in when I get in. You know I bust my ass all the time.

Supervisor: -Stop when argument gets through to employee-

Supervisor: I know you work really hard, and I appreciate that, but part of your job is providing customer service. If our customers are here at 9 and you aren't here to help them, it puts them in a tough spot. Your co-workers have to cover for you, and it isn't fair to them to double up their duties just for your convenience. I get rated on this unit's customer service performance and I like to be able to say that we are not just meeting all our guidelines and helping as best we can, but exceeding them. If this continues, we'll have to put you on a performance improvement plan, and that's one foot out the door.

Emergent Problem Example

Employee: -in tears- My car keeps breaking down and I don't have any money to fix it because my daughter has cancer and the chemo takes every extra cent I have*

TIL CSS has a 'Calculate' Function


I don't like to specify layouts in pixels, because display sizes are all over the place. I dislike sniffing the display size and adjusting parameters through javascript, because I feel like the document should take care of all of that for me.

Something that has been bothering me is I find myself specifying heights, widths, margin, etc in em, while I typically specify border in px. Maybe this is bad practice, but it's what I've been doing.

The trouble is when trying to figure out how big something is, I can add up the em, and I can add up the pixels, but I didn't know how to combine them.

Today I learned that you can combine them with the calc() function. calc( 10em + 5px ); will spit out the number.

It's only supported in bleeding edge browsers, but it's a nice feature.

The big gotcha is that you must have a space surrounding the mathematical operator, or it won't get parsed.

Happy Calculating!

How to Move Mercurial Changesets to a New Branch


This is me when I realize I've been committing in the wrong branch all day:

via GIPHY

Fortunately, there is a way to move changesets between branches, provided you haven't pushed your changes out to any other repositories. If you have pushed your changes out to the mothership repository, or your coworkers, the only way to pick up the pieces is to get all the users of the repo together and coordinate. That is beyond to scope of this article, and too hard for all but the smallest or dictatorial teams.

Problem Setup

Here at Reci-p.com we make recipes and sell advertising. I've been hard at work writing up new recipes. Here's what I start with:

$ hg history
changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

changeset:  0:6f5ef8f77a9f
user:       Gunnar Gissel <[email protected]>
date:       Sat Jan 28 11:30:00 2017 -0900
summary:    Added taco recipe

I want to add drink recipes, but they aren't ready to show to our valuable users, so I want to add them in a development branch. Unfortunately, I've been out all night sampling drink recipes and I forget to create the new branch!

via GIPHY

This is what I end up with:

$ hg history    
changeset:  3:398dfc004270
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added michela recipe

changeset:  2:852364587134
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added mojito recipe

changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

changeset:  0:6f5ef8f77a9f
user:       Gunnar Gissel <[email protected]>
date:       Sat Jan 28 11:30:00 2017 -0900
summary:    Added taco recipe

Fortunately, before I push all my code to the mothership repo, I notice I've been working in the wrong branch.

Move My Code to a Different Branch

The approach to fixing this problem is to create the missing branch, then create a patch from my new changesets, apply the patch to the new branch, then strip off the changesets from the incorrect branch.

Working in the same repository as above:

$ hg export -o patch.diff 3 2

I create a patch, which shows up as a file called patch.diff.

Create a branch off the newest changeset before I started adding new stuff:

$ hg update 1
$ hg branch drinks
$ hg commit

Patch the new branch I just made:

$ hg import patch.diff

This is dangerous, so make sure you are targeting the correct changeset. You can't easily undo this. You also need to have the strip extension enabled

Remove the changes off the original branch, so we can avoid mixing drink and food recipes:

$ hg strip 3

Phew, my repository is fixed. Now when look at the history I can see that I've got everything in the correct branch:

$ hg history -l 4
changeset:  4:8afca56c4940
branch:     drinks
tag:        tip
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added michela recipe

changeset:  3:357ff9545db
branch:     drinks
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added mojito recipe

changeset:  2:357ff9545db
branch:     drinks
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    drinks branch

changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <[email protected]>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

Now I'm safe to push and I won't accidentally pollute our recipes with drink mixing recipes

via GIPHY

Organic Artisanal Small Batch vs Impersonal Factory


There are a couple approaches a shop can take to coding. There is the "Organic, artisanal, small batch" programming and "Impersonal Factory" programming.

My transition from a small batch artisanal shop to an impersonal factory is recent. The change is still fresh in my mind, while distant enough to have some perspective. This is what the change from an artisanal shop to impersonal factory style coding is like:

Small shops and new devs take the organic route. This is out of necessity. You have to move fast, get code out the door and put food on the table and money in your pocket.

Infrastructure is the other reason. Small shops and new devs often have little infrastructure. There may not be a build server and a staging server. There may only be a laptop and production.

Organic, artisanal, small batch programming gets things done. If nobody sat down and banged out the first version of something we'd still be using slipsticks and filing cabinets. Moreover, while teams are small and have low turnover, the cost to artisanal coding is minimal. If the author is there and can personally explain where the bodies are buried to a small group, style differences are barely a hiccup.

As shops grow, the merits of factory style programming becomes clear. I don't mean the factory pattern. You GOF people can see yourselves out :)

At some point, around 5 developers, the overhead from introducing new developers begins to drag on the team's productivity. Different project formats and project that need leads to be responsible for building applications

The Solution

Stand up some shared infrastructure and standards. Pairing shared infrastructure and standards together is absolutely necessary. Without standards, shared infrastructure turns into a shared nightmare. Without shared infrastructure, you're left with standardized coding wizards mumbling into their own private grimoires. No chance of telling whether or not the standards have been adopted.

The bare minimum for effective shared infrastructure is:

  • A shared code repository

  • A shared build server

  • A place to put artifacts created by builds

  • A staging server for testing deploys before they make it to production

What each of these elements mean is different depending on the stack your shop uses. I'm in the boring enterprise world, so the stack I'm going to talk about is Oracle, Java and Javascript.

Shared Code Repository

The specific tool your team uses doesn't matter, so long as it uses a tool and you are all storing code in the same place. According to Zero Turnaround's 2016 tech survey Git (and presumably Github) is the clear winner for version contro, followed by Subversion. My shop uses Mercurial - we made the switch from CVS before it was clear that Git would win and haven't felt a strong need to switch just for the sake of switching.

I won't go into why you should use source control in 2017(!), but I want to emphasize that having a shared place where every developer gets and puts code is extremely valuable.

If you aren't using source control, get with the program.

Shared Build Server

In 2017, developers should not be deploying artifacts to production that they create on their workstation. Partly this is because it can be difficult to recreate the state of a dev's workstation at a point in time well enough to recreate the artifact. Partly because manual processes like these have a poor bus factor and are difficult to pass on to new devs. Mostly because computer time is cheap, dev time is expensive, and automation is easy.

A build server provides a place to define the build and deploy process. A place to record past builds. A place to store artifacts. One of the great benefits of a shared build server is that any developer can run a build by pressing a button. It's a powerful advantage when a new hire can do a build on the first day they walk into the office.

Keeping deployed artifacts around another great benefit. If you need to roll back, you can get the exact thing that was deployed last. No manual copying out to a network file share, no wiki notes about where to find the backups. Everything in one place!

Another benefit to a shared build server is that they can act like a friendly interface to cron. Devs can easily script a repetitive task and then pass off the scheduling to Jenkins. Jenkins keeps track of the scripts success and can email devs when there are problems. It sounds small, but this is a huge productivity booster.

Artifact Repository

A build server can hold all your artifacts, up to a point.

Build servers are good at holding artifacts that get deployed to production servers. Build servers are not good at holding intermediate artifacts, like library jars.

Jenkins can move jars between builds, but keeping track of which jar belongs to a build is difficult. Like spaghetti code, spaghetti dependencies end in tears.

Developers need access to intermediate artifacts, but build servers do not give easy access. Sane Java development uses Maven, which can go get jars, but it needs to get them from a repository, not a build server.

Nexus is the artifact repository my shop uses. It's an artifact repository that works with Maven and is self-hostable. Thanks to Nexus, new projects can use existing libraries and tools without really trying.

Staging Server

A staging server is a server where code is deployed before it is deployed to the production server. The simplest way to use a staging server is as a simple smoke test - deploy your code, boot the server. If it boots, A-OK.

A staging server can also be a test server. Deploy code to the staging server, then run functional and user-acceptance tests. The staging environment is designed to be just like production.

If you aren't using a staging server, you're staging/testing in production, which is dangerous.

Reflections on the Change from Small Batch to Factory

=======================================================

The change from a small batch shop to a factory shop has been positive. The speed of code development increased and the rate of recurrent bugs decreased. The straightjacket of Maven, and the Nexus repository encourage modularity and code sharing, which allows us to build more complex systems quickly.

On the negative side, the speed of creating a new project with new infrastructure decreased. More infrastructure for testing is required, which naturally increases the burden of rolling out new projects.

Increased automation has offset the infrastructure burden, but more automation remains to be done.

I wouldn't go back.

So You Accidentally Got Stuck in vi


It has happened to all of us. You're logged into a box, you want to edit some text and boom - vi is the only option. Or you're on screen and someone is beavering along in vi and then passes you the controls.

What do you do? How do you quit vi without changing things? How do you change text with vi? How do you save with vi?

Sometimes, the after typing a while you notice nothing is changing the way you expect and your cursor is jumping all around. You are probably stuck in vi.


Noobs Vi Cheatsheet

Vi has this concept of "modes". Keystrokes behave differently in each mode, so if you aren't sure what mode you are in, mash escape a few times before trying the following commands.

This isn't meant to be exhaustive, just enough to let a programmer dragooned into doing sysadmin stuff to self-rescue

  • :q!
    • Quit without saving
  • :wq
    • Save and quit
  • ZZ
    • Save and quit
  • :w
    • Save without quitting
  • A
    • Append text to the end of a line
  • R
    • Overwrite text starting at the cursor location
  • x
    • Delete the character under the cursor
  • D
    • Delete to the end of the line

Maven for Ant People


Ant people often find Maven completely mystifying. I know I did. I work at a shop that was, until recently, completely Ant based. We built complex build scripts, and we did with XML, the way God Intended™.

With Ant, you start from scratch. You can have your project set up any way you want - the directory structure is up in the air, the artifacts you want to create through building are up in the air, you can even bring in conditionals to change the build based on whatever you want. In short, Ant is the wild west.

Maven, by contrast, is a straightjacket. Maven requires your projects are laid out in a particular way. You have to use approved Maven directories, and you have to do things within the Maven lifecycle. You can't just write a bunch of targets and stick them together any old way like you can with Ant. Coming to a Maven project from the Ant world is very confusing, because the directory layout is slightly different, it isn't immediately obvious where dependencies are kept and when you try to run the pom as you would a build.xml, nothing happens. Maven calls much of this behavior "convention over configuration" which makes things about as clear as mud.

Project Layout

I think the first thing Ant people notice when they come to Maven is the directory structure is strange and different. Most Ant projects I've worked in have a layout something like this:

src/
|- com/
   |- example/
      |- package/
        |- packageclass.java
        |- test/
           |- test.java
      |- otherpackage/
         |- otherclass.java
         |- test/
            |- testother.java
deps/
|- guava.jar
|- jodatime.jar
app.properties
test.properties

Maven projects have a strict layout, like so:

src/
|- main/
   |- java/
      |- com/
         |- example/
            |- package/
               |- packageclass.java
            |- otherpackage/
               |- otherclass.java
   |- resources/
      |- app.properties
|- test/
   |- java/
      |- com/
         |- example/
            |- package/
               |- test.java
            |- otherpackage/
               |- testother.java
   |- resources/
      |- test.properties

Maven expects code and resources to be in the src directory. It further expects the src directory to contain a main and a test directory. Things in the main directory are headed for the ultimate artifact. Things in the test directory are test data, or junit tests, or other test code that we don't want to include in the ultimate artifact.

Both the main and test directories have java and optionally resources directories. Java code goes in the java directory - this is assuming you are developing in Java, of course :)

Resources, like properties files, or test data, or FindBugs exclude files go in the resources directories. If your project doesn't have any resources, you can dispense with these directories entirely.

The trick with Maven vs Ant is that Maven absolutely requires this directory structure. It plain won't work without it. At first, to my Ant eyes, that seemed needlessly constraining, but the more I've worked with it, the more I've liked it. Any Maven project has the exact same directory structure, so as soon as you open a Maven project, you know where everything is, regardless of who developed it. In my Ant shop, it helps to know who the first developer was, because each dev lays out a project slightly differently. While this gives the code a charming old world village feel, it makes ramping up new devs a slow process.

Lifecycle

Ant is made of build targets. Targets are an xml element that define a unit of Ant behavior. Targets can depend on other targets, so you can have some kind of order to your build. You can define any number targets and call them whatever you want, and set up whatever hierarchy of targets makes sense to you.

All that said, I think this might be a 'normal' structure for an Ant project:

<project>
    <path></path>
    <target name="init"></target>
    <target name="compile" depends="init"></target>
    <target name="test" depend="compile"></target>
    <target name="jar" depend="test"></target>
    <target name="javadocs"></target>
</project>

Reading over a Maven pom reveals no clues at all as to what the happens during the build. Here's a schematic example to contrast with the Ant example above:

<project>
    <meta stuff defining the project></meta stuff>
    <build></build>
    <dependencies></dependencies>
</project>

That's it! When I first read a pom, after years of reading Ant build scripts, I couldn't figure out what was supposed to happen. It doesn't look like anything is going on - no compiling, no jarring, certainly no testing.

Again, as with the directory structure, Maven is implicit. It has what Maven folks call a "lifecycle."

Vocabulary Lesson

Ant people will benefit from a short vocabularly lesson to get up to speed with Maven jargon, and to get some googleable terms

  • Lifecycle
    • The process for building and distributing an artifact
    • There are three lifecycles
      • default
        • Default builds and deploys your project. Deploy might trip up those of you who are used to putting things on servers. In this case, it does not mean 'deploy your webapp to Tomcat.' Instead it means 'deploy the build artifact to a Maven repository'
      • clean
        • Clean cleans up your project, by deleting build products and intermediate clutter that the build creates. In practice, this mostly means it deletes the target folder that Maven builds create
      • site
        • Site creates the documentation for your project's website. Javadocs, junit test reports, findbugs reports and that sort of thing happen in the lifecycle
  • Phase
  • Goal
    • Phases are made out of plugin goals. If you read through a pom, you'll notice that build element, for example, often contains a number of plugins. These plugins define what happens in a phase.
    • Goals can also be used independently of a lifecycle, to allow for a finer grained control over the build

Using the Build Lifecycle

To use the build lifecycle, use the Maven command line and pass in any lifecycle, phase or goal name.

Examples

  • Lifecycle
    • mvn default
  • Phase
    • mvn package
  • Goal
    • mvn dependency:copy-dependecies

You can pass multiple lifecycle arguments to maven and it will execute each of them in turn

Dependencies

Dependencies is one of those places where I don't think there is a standard Ant way of dealing with them. Ant people all know the labor of keeping the jars their project depends on in synce, and how hard it can be to distribute these jars to a team of developers.

I've seen a couple approaches in the Ant world. Sometimes there is a folder on a shared network drive, and in the wiki for the project it tells devs to grab that folder and put it somewhere special relative to the project. Other times, the jars are just included with the source code in the repository as big binary blobs. Sometimes the jars get their own version control repo and devs are supposed to put the dependency repo in the same directory as their project repos, so all dependencies can be reached as ../dependency.jar. I've even seen a giant zip file with a pre-configured directory containing eclipse with all the projects set up and a directory of the repositories included in a known location, with a script for syncing the known location with a central server.

My point is, Ant doesn't really have a way of dealing with the problem with dependencies. Regardless of what you are doing, you also have to manually compile the classpath and include that in your Ant file, which is another interesting excercise.

Where Maven shines, and what initially brings people to Maven, is its dependency management. Maven will go get all your dependencies for you, build a classpath and can even make a fat jar for easy releases. The only thing you need to get all the dependencies for a project is Maven, and a pom.

Maven also has the concept of "scopes" for dependencies. Indicating that a dependency (like junit) is only required for the "test" scope will leave out that dependency when building the final artifact.

Basic Maven Usage

Knowing how Maven is supposed to work is all well and good, but how do you actually use it?

In most cases, a developer probably wants to make a jar, war or ear that they can use or give to someone.

Here's how you do that from the command line:

mvn package

From Eclipse, right click on the project and select 'Run As' > 'Maven Build'. The first time you do this, it will bring up the Run Configurations dialog. Type 'package' into the goals field and run the build.

You might notice that these commands don't run the whole default lifecycle. They skip the verify, install and deploy phases. Those are a little more complicated than the basic differences between Maven and Ant that I try to illustrate here. A separate article is probably required to talk about what installing and deploying mean in the Maven world, how they interact with IDEs and when they should be used.

2017 Goals


2017 Goals

So, given how 2016 went, what are my plans for 2017?

I'm going to stick to the same basic plans, but scaled back a touch so I have some chance of hitting them.

Blog Goals

  • 26 blog posts this year
  • Measurable blog traffic
  • Create some tools for people
    • Talk about tools

Personal Goals

  • Be present in year 2 of the baby's life
  • Figure out how to manage a toddler
  • Bicycle
  • Lose 20 lbs (I lost 40 last year, so this is just a touch up to get where I want + a little padding for the occasional dessert)
  • Pare down household to a manageable level of stuff
  • Tweak savings plan to account for global instability
  • Pick one worthy political cause to be active in
  • Keep up sociability

Professional Goals

  • Learn how to build Javascript build/packaging/deploy systems
  • Get a grip on messaging and Service Oriented Architectures
  • Beef up supervisory and leadership skills
  • Succesfully project manage rollout of new Sealandings client
  • Project manage migration from legacy Flex monolith to SOA and Javascript system

Summary

Having written all this out, I notice I have a lot more personal goals than blog goals. I think that's ok - I don't intend to live this blog. I suspect blogs are more interesting when they are written by someone who is living. I also notice most of my professional goals are along the project management and architectural lines. I think that's most likely normal at this stage in my career. My techincal skills are sufficient to the tasks I do, so it seems natural to want to focus on the meta-technical and support skills.

2016 Year in Review


2016 Goals In Review

How'd I do? Not well. I set some big goals and whiffed on all of them. Essentially, my problem is that I didn't blog enough (or really at all). I think the trick is something like how to keep a slow garage project rolling. Every day you need to get in there and tighten one bolt, or clean one thing, just to keep momentum up. Do the big projects while you can, but if you can't, keep the ball rolling with small stuff.

I suppose I could chalk this up to acquiring two new responsibilities in 2016. I had a baby - great fun, great responsibility, takes up far more time than I had anticipated, not that I begrudge a second. I also picked up an acting supervisory role at work, which takes up a surprising amount of my bandwidth for planning and organization. Not sure it's as fun as a baby, but it's a change and pretty interesting, to boot.

« Page 3 / 5 »