AngularJS UI-Router

AngularJS contains basic client-side routing functionality in-the-box. This is great for small applications and simple websites. However, the functionality needed for larger enterprise solutions is lacking. UI-Router is a third-party solution built for large AngularJS applications. At a recent St. Louis Angular Lunch Mark Volkmann of OCI gave a talk on using UI-Router in your Angular applications. A video of the talk is available below.

 

Transcript

Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.

Mark: But I'm going to go over some basic things and then what I think are some more advanced things. So you can be the judge of that, and then we'll have some time for discussion after this. We've got 37 slides. We can't go through 37 slides in half an hour. So I'm going to skip some things and really try to focus on demonstrations and looking at code that runs those things.

So the basics are that in older versions of AngularJS, it came with the routing service, this thing called $routeProvider. And you can still use that but it doesn't come with Angular, and couple of the problems with that is that it didn't support nested views or sibling views. So now let's pull out core, you download it separately if you wanted but there are other alternatives and ui-router seems to be the most popular one probably because it does work nested views and sibling views.

If you want to use this, the few basic things you do to set it up. One is of course you have to pull in the JavaScript for this thing, and so that's the first box up there. Second you have to say that you depend on this. And I bet this is the thing that gets most people the first time they use it because the thing is called ui-router. But when you set up your dependency on it, it is ui.router, so catch the dot there. And then you set up views on your pages and the views can have names but they don't have to and so here is just a very simple one. It is a div tag but it is marked with the ui-view directive and you don't have to put any content in it. But if you do, then that's what will display until you put something into the view and so that can be a useful thing to have some default content. Then the biggest part of this is setting up the configuration so I'm doing that in the app.config and there are two services I need. I don't understand, maybe someone else can explain this to me why I choose services. Because I don't know why I can't set up my default state on state provider, why they didn't make the API that way. But it's not the way it is. And so I'm telling you with UI router provider, that if I don't tell you what state to go to, the default is whatever I specify there. Then the most important part is $stateProvider. I'm defining my states.

So in the older router service, states were defined by URL. You say you want to go to that URL now, you see that route. In ui-router, your states have names, and when you say you want to transition it to the state, that can be in a lot of things. In the most basic case, it means you're going to a route that has a certain URL associated with it. You want to use a certain controller and you want to use this template to display something. That's the basics. But beyond that, there are a lot of options we can specify when you configure a state and I've got four slides on this. I don't know if I have the time to talk about any of it. So blah blah blah blah and so let's see some real examples of using some of those options.

So first of all, assuming I have configured my states, you've got to have a way to switch from one state to another. And there are three basic ways to do this. One way, is that you get hyperlinks on the page and when the user clicks that hyperlink, you want to go to a new state. And so you do that by using the ui-sref directive on an anchor tag. So instead of href, it's ui for ui-router and s for state, switching through that state. So you don't give this a URL or a path. You give it the name of one of your states. And then in your JavaScript code, another alternative is to use the $state service, you call go and pass it the name of the state. And then last, if you actually navigate to a URL that's associated with one of your states, then you go into that state. If you could do it from JavaScript with $location.path or you can just type the URL in in the browser address bar and you'll go to that state.

The most basic example is just using the anchor tags that I talked about. So I have this kind of dummy weather forecast here and there are two ways you can look at this. There's the five-day forecast and the hourly forecast. If you just click the hyperlinks to switch between them. And so let me demonstrate this thing working. And so here it is, then I can click to look a the hourly forecast, and I can go back to the five-day forecast. Pretty basic stuff. So my view is that main part of the page, not the heading at the top. And then I'm able to switch between those two states.

So let's look at the code behind this. So here is my main HTML page and I'm pulling in of course Angular itself and then ui-router part, and then my own JavaScript weather.js and I've got some CSS. We don’t take the time to dig through the CSS. We just stress some nice things for us but then I have this div with the links in it and there you see the ui-sref. So I can switch between the hourly and the daily states, and then at the bottom, I have this div that's going to hold my view my view. And so I switch to a state, it's going to populate that div with the UI view directive on it.

Here are my two partials. So when I set up the states, I'm going to say these the templates I want to use, and so one for the five-day and one for the hourly. And this is some pretty basic Angular stuff here. I'm assuming that on my scope, I'm going to have day forecast which is a collection of day forecast objects. And those have properties on them of day, and the high and the low and similarly for the hour forecast. That's going to be on my scope and I can pull out the hour and the temperature.

On the JavaScript part, I'm setting up my main module which is called weather and you see my dependency on the ui.router module. And then I have my factory for the weather service. Now in a real application, I'd probably be making a REST call. I'd go out and get real data. But I'm just adding some dummy data here to demonstrate these ui.router. So I've got an array of forecast that I'm pushing in to that, a bunch of objects that represent an hourly forecast and then just returning it. And then similarly for the daily forecast, I have a day and a high and a low. And this is a very typical way that I set up my Angular services where I create this empty object that I call Service. And then I attach it onto a functions onto that and then at the end return that Service object. Of course when you define a service, if you want, you can return just a single function but it seems pretty common for me though and I want my services to have more than one function so that's a pattern I usually follow. And question, yes.

Kyle: Other than it's a ui-router, but there's this thing called factory, this thing called service. And unfortunately, it seems like your standard terminology that every [6:46 inaudible] pops is the safe service literally using factory. Do you have any insight deeper than what you get from the factory application that not touch on whether you’re supposed to use factory and service? You kind of accomplish the same thing in fact.

Mark: So it turns out that there are four different ways to define service and Angular and I have slides that show examples of these four different ways. But in my experience, it seems like 99% of the time people use factory. And I have yet to run across the case where I wanted to use one of the other three ways doing it. So maybe that could be a future talk or kind of a group discussion and talk about these different ways of…

Kyle: Paul is always using [inaudible].

Mark: I do. Yeah.

Male 1: One of that was on both parts of this is things people like to at least talk about the [7:30 inaudible]. Kind of a philosophy behind when you use different . . . .

Mark: Right, so even though I had value than the other three ways if I show examples of using them, other people might say, “Oh that's why I would use that.” So yeah we'll give you that. Then we get down to my controller, my weather controller at the bottom and I'm injecting into this, my weather service and then a colon, and this function. I'm filling that data on this scope so that I can display it in my view.

And then the most important part of this is defining my states. And so I'm setting that the default is to show me the daily view and then I have my two states defined. And each state has a URL associated with it. I'm telling it what controller I want it to use for getting the data that I'm going to render and then what is the template they'll use for displaying that view. Any question about that? This is really like the most basic thing here to do with ui router.

And then we'll talk about having sibling views when you switch to a state. It could be that your page contains multiple views and you want to update all of them when you go into this new state. And so this stage here has four views on it obviously, two borders up there. And there's a little bit of functionality here. When I'm in this state on the left and I click, red, green, and blue, it's going to change the color of the text that's right there. And when I'm in this state, and I click these, it's going to change the size of that text. So let me demonstrate that. Local 300. I can change the color of that text. And I'd switch to the other view, change the size of that text.

Okay so here is my main HTML and a big difference you'll notice here is they don't have just one UI. I have four of them and they have names. So I'll be referring to those names when I set up the configuration from my states.

So here's the CSS, we can skip past that. And then my partials, I have a lot more partials this time. I have four areas of the page so I have two partials for each of those areas. The headers and the body part, and the footer are all pretty simple and then that nav that was off to the left where I had all my functionality has some hyperlinks. And when you click those hyperlinks, and when you click those hyperlinks I want to call some functions that are on the scope to change the color or change the font size and then when I want to change what view I'm looking at when I'm in the first view, I want to be able to switch to the second one, or I should say state. When I'm in the first state, then when I switch to the second state, and when I'm in the second state, I can switch to the first state.

So on the JavaScript for this, again I'm creating my main module and I'm depending on UI router. Then in the controller, I think I'm doing something that people frown upon. I'm doing a little bit of dom work here but it's simple stuff. I'm just changing some CSS properties. So when I call change color on the scope, there has to be color name. And it's going to find all the elements that are sections and change their color. And if I go back to my main HTML, the only place I have sectioned right here that's the body, changing the color of everything in the section when I call that. Similarly for changing the font size, find all the sections and change their font.

And then for configuring this, that is really the interesting part, I tell it that I want to start in the first state, and then for the state provider, the green part there is what you in the boxes on this slide. Here's the definition of the first state. And the big thing that is different here is that rather than tell it about template URLs and controllers in that top object that I'm passing in, I have a views property which is not just has all of that in it. And the properties of the views I have picked are the names of my views. And so those are the four names I had and then each one has a template URL. The header and the body, and the footer, there's nothing active going on there. So I don't have a controller. But I need a controller for the nav part because I need to call some functions that the controller adds to the scope when you click those things. And so the second stage is very similar to that. Any questions about that? Okay.

Audience: And we have to provide those alternatives that are really the same like headers and shoulders are really the same.

Mark: They're not the same. I mean these two states.

Audience: Yeah.

Mark: Because there's one here, that's header one and this one uses header two.

Audience: Yeah I guess maybe I missed something. It looked visually like they didn't change at all.

Mark: Oh no, yeah let me go back to the browser. See it says header 2 and footer 2, and when I switch views, that changes in all four sections.

Audience: But you didn't specify those with [12:57 inaudible] when I switched states?

Mark: Right, nothing would happen.

Audience: So if I had sections on my pages, that are kind of static, I could just not touch it.

Mark: Although, probably in that case, you want what I'm going to talk about next which is nested views.

Audience: Okay.

Mark: Yeah. Alright so nested views, and this is maybe one of the more complicated parts of this. And I don't believe I'm describing all the options here but it seems like the common way of defining nested views is that there's a name of a state that is your parent state. And when you define one that is nested, you use a dot for that. So the presence of a dot in the name is what makes it be considered a child view. These URLS can be parameterized. Just by putting the colon there means that you'd be able to extract some data from it and do something different based on the passed in. One of the things I ran into is that you have to be careful about the order that you define these states. You have to define the parent states before the child, or else you'll get this error message but it doesn't really give you much of a clue of what you did wrong. Okay now, and if you do that parameterized URLs then you use the $stateparamservice to get to those values.

So this is how I want to demonstrate this. We have the Volkmann diner. At the top, I have three menus you can pick from, breakfast, lunch and dinner. And when you pick one, I'll display a different selection of items below that. And then when you click one of the hyperlinks for a food item, then it will show you a picture of it. And so the point of having nested views here is that we've already listed whether I want to see the spaghetti or the pizza, I'm still going to be in the dinner view, and then below that, nested inside that view, I'll show you a picture of the spaghetti or the pizza. So here it is and I'm looking at the dinner menu. I can switch to the lunch menu and the breakfast menu. Notice it says click an item to see the detail. That's the default content in that nested view. And then when I click this link, it gets populated with some other data. But that is the nested view inside the breakfast view.

Let's walk through that code. So in my main HTML, first I have the dinner controllers specified in my HTML because that's going to add to the scope, the string bulk, so it displays who's done or who this is. And then I have my hyperlinks for switching my state for the different meals, and then, finally this is where I'm going to display the menu but this view is going to have a nested view inside it. We'll look at the CSS.

Here are my partials. These are what I have for each of the meals of breakfast and then I'll have a bunch of these. I'm showing just a few here but you can add as many as you want there. And then similarly for the lunch, and for the ones that I do have a picture to show then I have a nested view. You notice the dot in here that's what makes it a nested view. So I have pictures for some of the food items. And then here is where I’m going to to display it. So notice that I have a ui view. It doesn't have a name, and neither did the parent one but it can keep that straight because they're nested. And so when I tell it to display the omelet, it's going to know to put it inside that view. I have my default content for what you see until you click on those.

Here are the partials for the actual food items. So they just have an h4 telling you what it is and then an image stack.

And then on to the main JavaScript, I set up my main module which is called diner and then I'm depending on UI router and the diner controller. All I'm doing is adding the name property to the scope. And then meal controller, I'm just putting this in so that I can demonstrate the use of the go method to change the state. So I'm saying wait two seconds and then switch to the lunch menu. And maybe I have that commented out of my actual code but that's just an example of how you can switch state from the Java code.

So as in all these examples, the most interesting part is setting up the state provider and so I have the parent state for breakfast and then it's got its own template. And then here's a child state for the omelet and notice the URL is just /omelet. Well you can't go to /omelet but you can go to /breakfast/omelet. So the URLS of the child states are appended on to the URL of the parent state. So the same kind of thing is happening with the lunch and dinner being parent states and lunch.pizza, a child of that, dinner.pizza, a child of that one. And that's all there is to that. So if I go back to the browser, I want to show you that if I go to the lunch menu, I'd see that there is pizza there. So if I go back to breakfast, and I go up to here in the browser, where I can type lunch/pizza and it should switch to showing the lunch menu and show me the picture of the pizza. And that's correct. Any questions about that?

The last thing I want to talk about is resolve. So the point of resolve is that when you switch to a new state, you don't necessarily want to begin rendering immediately. And that's because maybe some of the data that you're planning to render is going to come from a REST service and it's going to take a while for that data to come back. And your page can actually look pretty bad if you don't wait for the data to come back first. So let me show you an example of a bad page. So you notice that the page is going to come up immediately. Then some data is going to fill in and then some other data is going to fill in. Okay, so that's not nice. That's not to say that there won't be cases where you want to have your page up there and data is dynamically being plugged in so this is kind of a design sort of thing whether you want it to behave this way or not. But if I want it to wait until that data comes back, before I display something, I can do that. And so let's walk through the code first and then I'll show you that working in this better way. So I want it to display a table at the top, all that data's coming from a REST service and then I have a list at the bottom that's also going to come from a REST service.

So in my main HTML, there's nothing really special going on there. I have just one view but notice I have some default content that says the view is loading. So when I fix this, that's what you'll see initially. And then when I have the data, it'll all come up at once. And then, this is a partial that I'm going to display and so I’ve got the table at the top where I display all the marathons, and then there's the list of runners at the bottom. So marathons is going to be a collection that is added to the scope at some point. And runners is the same.

And then in the JavaScript, I'm creating my main module called marathons. I'm depending on ui-router. I have one service called the marathon service and this is an example of using the $qservice, you'll see that on the next slide. And also using a time out. And what I'm trying to do here is simulate a REST service without writing one.

So I've got the getMarathons as a function on my service. And normally right here, what you'd be seeing is perhaps a use of the $ http service to make a call-out to the REST service and that would return a promise and then you could wait for the promise to be resolved. But what I'm doing instead is I'm saying I'll create my own defer object and then I'm going to immediately return the promise. So something else can choose to wait on that and in this case, it's going to be the ui router and it's all of the functionality that's going to do that for me, yeah. But it wouldn't be any different if I was using $http.get to call the REST service. So I use the timeout service and say I want to wait 1 and a half seconds and when that's up then I'll create this w array of data. And then I call resolve and now that comes the value to get back from the promise. Similarly for the runners, it created a defer object. Immediately returning the promise. Then when one second elapses, return this array from the promise.

So in my controller, I want to call those two things but I don't have the data available until the promise gets resolved for each of them. So when I call to getMarathons, I'm getting back a promise. I call then on it, and I give it two functions: one for success and one for error. And if it's successful, then I attach that data onto the promise. And notice that the controller we're looking at here is bad controller. That's because it's going to render the data as soon as it becomes available. And it doesn't care about what other services you may be invoking. They'll all be handled independently. So as soon as I get the runner data back, then I’ll add back to the scope. And of course, the way I have it set up, that's the one that will come back first, because that one only waits one second and the other one waited a second and a half. So contrast that with the good controller where I'm injecting into the controller marathons and runners and we're going to see show that happens through resolve on the next slide. But here, we're just trusting that something is going to give me this data and when it gives it to me, I'm not waiting anymore. I've had it and I attach it to the scope and I'm ready to render that view. So at the bottom there I'm starting to set up my states and saying that I'm going to begin in the marathons state. And turns out in this example, I only have one state, but the reason I'm using ui router even with one state is that I want this resolve capability.

So at the top showing I'm showing setting this up to use the bad controller and it's really simple. I just tell it what URL is associated with the state, what template I want to use. Notice both of them are using the same template and then that one has the bad controller that makes the calls and whenever the data comes back, attaches it to the scope. In the good one though, I have this resolve property. And I'm telling it do not try to render this view until all of the promises return down here, come back. So you've the passed the resolve an object where the keys are the names of things that can be injected into the controller. So marathons and runners, and if I go back to the previous slide, that is where these things are coming from. Those names have to match up. And then I return anything that will give me back a promise and these functions give me back promises. So I'm all set and that's going to know that it should wait until all of these things get resolved before it attempts to render that. Yes.

Audience: So I'm a noob at all this but I know [24:53 inaudible] in some places.

Mark: Yes and the way to fix this is that the key thing is wherever you're injecting things like this, —i's a bit ugly—but what we have to do is instead of passing in a function, you have to pass in an array where the things in the beginning of the array are all strings. There are these names just in quotes. And then the last thing it's the function. And so then the identifier and go ahead and change these to something else but because it has the actual name as a string, it's able to view on that. So you have to write the code a little different to prepare that.

Kyle: Mark, we've stopped doing that, and one of the process is switching projects to use a grunt plug-in to perform that transformation programmatically.

Mark: Oh cool.

Kyle: What do you think about that? On one hand, we're kind of like let’s step farther away from us writing the code that runs. In other hand, the duplication between the strings, it’s just morally offensive there, the duplication in there.

Mark: I like what you’re saying because the code is just ugly the other way. Did someone create that plug-in or did you create it?

Kyle: We didn't create it. It's just sitting out there. It's a thousand other pre-1.0 plug-ins on GitHub.

Mark: I'd like to know what the name . . . .

Kyle: Well somebody here, he can probably tell you what it is.

Mark: Yeah.

Audience: One of the things that we run into is we resolve very heavily and that means that we have a lot of things like this where we don't have to pester. Can you come up with a good way to [26:24 inaudible] the actual outside of [inaudible] that some people messed up the resolve blocks?

Mark: Well I guess I have to admit that my own personal preferences lean heavily towards integration or end test. And so if I wrote that kind of test, if I'm understanding your question right, that wouldn't be an issue because I'm just going to run the whole code and look at what ended up on the dom.

Kyle: So you're talking about testing whether getMarathons works?

Male 1: Well so here's an example that's very simple for this, just one line to return Marathon service to getMarathons. But like if you were saying doing a resolve that’s taking state parameters something like that and then performance or some service calls off of that, that might actually be something significant enough.

Kyle: So my preference would be if that function was any less trivial than that, it would be to make it delegate to something in some other code.

Male 1: Kind of like the service that . . . .

Kyle: Yeah right and test that.

Mark: Okay so I'm pretty much done. The only thing additional that I want to say is that there's some events you can listen for that are associated with this and it's pretty useful for everybody and especially listening for state change errors and getting some details so that you know that there was an error when you were in this state and you tried to transition to this other state, or maybe you tried to go to a state that you never defined and you get a state that you’re done. So these are things you could listen on and these are all on the root scope and so you could listen to them from any scope and you'll get that. And so there's some examples of the syntax there and how you set that up. And then you can listen for a view to finish being loaded here. You get the view content loaded event. That's all I've got. Did I make it in half an hour? Probably not.

2014 Guide to Chromebook Apps

One of the challenges when entering the Chrome ecosystem is the lack of familiar names and products. The go-to apps on a Chromebook are not the normal fare, and for good reason. Apps on a Chromebook exist in collaboration with the internet at a very deep level. On other platforms, internet goodness is considered a feature, not the root. This does not mean that Chromebooks are useless without internet connectivity. They are not “just a laptop that runs a browser for an operation system”. If you have not figured out the fallacy of these myths please stop reading, I cannot help you.

Previous Chromebook Articles:
A Year With Chromebooks
I Love This Chromebook
A Misunderstood OS

Following is a quick rundown of the apps that I rely upon in my daily use of my Chromebook Pixel. It is very rare that I desire any legacy PC and certainly rarer that I cannot accomplish a task without one. This has happened. The most notable being the lack of support for dropbox zip-format lunacy (well documented). In order to access an archive I had to use a Windows machine. Other than that I cannot think of a time in the last 6 months I ran back to an old platform. So on with the list:

1 Chromecast ($35 Google Play)

ChromecastYes, my first app is not an app. This is an example of the mind splitting differences between the Chrome ecosystem and Windows or OSX. This piece of hardware “behaves” like a supercharged app. In a couple of clicks you can stream your favorite media or mirror your screen for anyone to see. All wirelessly and painlessly. A significant improvement over Apple TV and other media boxes at a fraction of the cost. A must have.

2 Google Drive (Free)

Drive really represents a number of apps rolled into a single interface. Docs, Sheets, Slides, PDF Viewer, Cloud Print are just a few. These offline apps are incorporating many of the benefits of the Quickoffice acquisition by Google. It is important to understand the offline capability, it is best used for new content creation and premeditated editing sessions. If you store everything in the cloud you cannot edit it if you are offline. This is not a failing of the app, it is a reality of the architecture you are using.

3. Lucidchart (Freemium, Subscription)

lucidchart-06-535x535This application is a great value and an example of a rethinking of a traditional space. Lucidchart rethinks how we build diagrams and incorporate them into our work. Not only is it web-centric, but it actually improves upon the standard set by Visio, SmartDraw and others. The tools are easy to understand and use, the user base is broad with lots of templates, and the integrations with JIRA and Google Apps are well done.

4. HootSuite (Free for individuals)

HootSuite is an excellent aggregation point for social networking. I like to consolidate feeds and HootSuite is an excellent place to do that with support for almost all of the top social networks. The interface is well thought out and translates well to touchscreens. Well-designed!

5. Chrome Remote Desktop

If you work with other machines this app is automatic. There is not a lot to say other than it works like it should and is constantly being improved upon. Looking back it was quite spartan a few months ago and now has a number of additional useful features.

6. Pixlr Editor (Free)

pixlrOutstanding photo editor that rivals Photoshop for most users. Excellent community support. I was getting a little irritated by a Chromebook bug up until a couple of weeks ago, but that seems to be completely gone. I use this app a couple of times a week when working with images. Hard to over-emphasize the intuitive toolset if you are a Photoshop or GIMP user.

7. Keep (Free)

Up until earlier this year I was an avid Evernote user. I even paid for the professional version. Keep has removed Evernote completely from my consciousness. I use it extensively for writing articles like this one. I can edit here, on my tablet, or even on my phone without a thought. Everything I do is automatically backed up in Drive and goes into my normal archival system (more on that in a bit). It still needs a few more features to be indispensable but like other apps listed, enhancements are coming regularly.

8. Cloud9 IDE (Freemium)

Cloud9IDEAfter waiting for quite a while, Cloud9 emerged as a viable alternative to a traditional IDE. If you are working with supported tools, the FTP and Git integrations are powerful. I am currently using Cloud9 to develop my personal and business websites. I am hoping to branch into some web app development next.

9. Various content consumption apps! (lots available)

The ones I use, not in any particular order:  YouTube, Netflix, Google Music, Google+ Photos, Kindle Cloud Reader, DropBox, Google Play Books and Movies (viable option to Amazon).

10. Spanning Backup ($40 per year, the link sends you to a discount code saving you $5)

Unlimited backup of your Google account and Drive storage. An automated, daily, incremental backup of your mail, contacts, calendar, and files. There is not a size limitation and it is easy to export your files for local backup at any time. My personal backup solution has morphed radically with the switch to Chromebooks. Instead of trying to carry all my files with me I let Google curate them in Google Drive. Spanning backs them up each day and about once a month I export them in a zip file to a local external drive. Periodically I make a copy and place it in my safe deposit box. This way I have 4 copies in different places without a lot of effort. Thinking this way requires a paradigm shift that I will write about in the future, for me it has been very freeing. I no longer spend large chunks of time performing administrative tasks to protect my files. I execute some simple processes at regular intervals and I have peace of mind that my photos will not just disappear in the cloud.

I am sure my list of top apps will be different a year from now. As I work through this article I am stunned by how little is missing. And to those naysayers out there, most of the apps listed above work offline. There are obvious exceptions but a lack of internet connectivity does NOT keep me from being productive. Enjoy the freedom and flexibility you get with a Chromebook, many of us have dreamed of these features for most of our techie careers.

Merry Christmas!

Code Reviews – Use a gate, not a drive-by

Does your team / project use code reviews? If not, I suggest starting today. There are countless resources online about how and why to do code reviews; there are numerous code review tools, open source and commercial.

Who should review code? A great question, perhaps for other blog posts.

When should code be reviewed? Hopefully also great question, and the topic of this blog post.

Context

When making a recommendation, I’m trying to get in the habit of always mentioning the context, the situation in my work from which my recommendation arises. This is in response to seeing frustration when people try to follow advice, not realizing that their situation is completely unlike that of the writer.

Here ate Oasis Digital our context is mostly complex line-of-business applications, which are expected to live for a long time and are important to the business operations of the companies that use them. We are almost always replacing an existing system (which a company relies on) or expanding an existing system (which a company relies on).

Another critical part of our context: we have flexible and fast source control, and a group of developers who have learned how to use it competently. We have decoupled source control from build automation, so that we can perform builds of branches in progress without merging them to a mainline. We can achieve a sufficient degree of continuous integration without actually putting each change directly into the same code line.

Review Code Early

How long after code has been written should it be reviewed? Hours or days, not weeks. For this reason, we commit and push code frequently (to a disposable branch of course) where others can see it for early review. It can be a challenge to get past the ego-driven desire to make code perfect before exposing it to the scrutiny of others, but it is worthwhile. By exposing code to review and comment early in its life, a developer can get useful feedback early in a unit of work.

peer-reviewReview Code Often

Another anti-pattern of code review is to look at a given proposed change only once or twice. That is sensible for a very small change, but for a complex change to a complex system it is best to perform code review repeatedly as work proceeds. For example, consider a feature that takes four weeks to implement. Our typical approach, which I recommend to others (if their context is reasonably similar to ours, see above) is something like this:

  • Review the code a few days in.
  • Review the code progress once a week or so.
  • Review the code when it is nearly ready to be called “done” and to go in the mainline.
  • If there are fixes needed, review those promptly so as not to cause delay.

Review Code Now

Code waiting for review is a drag on the speed of development; the right time to review code is now. A quick review now is probably more valuable than a deep review deferred.

Review Code as a Gate

In some organizations, code review happens asynchronously and sporadically, after code is already part of a project. This risks software quality collapse. Once code is in the mainline of a product, it is arguably too late to review. If a reviewer finds a problem with code already in a system, there will be pressure to sweep that problem under the rug and move on, or to convince your that is not really a problem, or that quality doesn’t really matter, just this once.

The right answer is simple: use code review as a gate through which code must pass before it can become part of the mainline of the project. Use your source control system, and stack up proposed code changes that are nearly ready to go into the mainline. Then periodically perform your review process (whether it is in person, remote, synchronous, asynchronous, etc.). Once the code passes a final review process, then it goes in the mainline. If there are serious problems, the code sits in a branch, a developer improves it, and it tries again next time.

We have had excellent results with this approach, maintaining software quality for years on end even through ongoing development team turnover.

 

Using JIRA: Be a Winner not a Loser!

When using a tool like JIRA to curate your project management processes it is important to use it effectively. It is very easy to move off of the “happy” path and into the weeds that cycle a project into chaos. I want to touch on a few of the pitfalls and also highlight a couple of creative ways to use it.

Pitfalls

#1 Death by Ambiguity

bigstock-Stuck-under-a-question-mark-11139323When putting your work into any issue tracker it is always tempting to create items that are not only unhelpful but ultimately damaging to your project. If you are early in a project and dividing large chunks of work for future definition, items like “Email Notifications” or “User Profile Page” are not good for the project. Items like this feel good at the time, a list of them covers the breadth of the project, but they will hurt even in the short term. How can a developer know where to start with these? They span major components of an application, UI, server, client, and more. In many teams the same developers will not even work on these different components of the software.

Later in the project these can be damaging as well. For example, a few months down the road a developer is now taking on responsibility for the administrative screens in your shiny new application. They keep noting the need to work on email notifications but do not know where to start. The naming of this ambiguous item gives them the false sense of security that it is noted and will be handled, when nothing is further from the truth. Preliminary design work may desperately need to be done for the notifications to be ready in a future chunk of work (milestone, sprint, release), yet nothing moves. Developers that do not have the responsibility for other parts of the system touched by the item cannot, on their own, break it up into pieces.

Some forethought and design need to be taken early in a project to avoid these problems. Use a standard that works well for your organization. Break it up into meaningful chunks that will match up with your development team structure. For example we might enter the item as a part of other related work tied to the client, server, administrative screen design. This way the feature heads into the design process without cluttering and confusing future waves of work. Another solution is to specifically identify a design item for the feature. This is valid work and is worthy of being tracked as a part of most projects.

#2 Death by Stagnation

bigstock-Businessman-Pushing-Stone-54105938Another major pitfall is not breaking up larger items that are well defined. A large irritant to a customer (internal or external, there is always a customer) is seeing the same item stagnant and not moving for months. We try to keep most of our items at a size that can be completed in a week. This way the customer can see progress on a continuous basis. This often means that a particular item might be broken down into many parts, yet there is often resistance to this extra work. Not breaking up these components can leave your progress hidden from your customer.

All customers want to know when their shiny new application will be finished. This is a normal desire and one we challenge constantly in the custom software world. We all know we cannot control outside factors that impact or drive a customer’s business. In the course of a project, stresses and pressures will often come and your customer may begin to scrutinize the project in a different way. The game changed and you are still moving along. When the customer comes to look, will you have 20 large items moving slowly or 150 moving quickly through your process? I would much rather have a large number of items being clicked off weekly. The customer can see steady (sometimes daily) movement. They can predict themselves when the application might be completed.

#3 Death by Silence

bigstock-The-words-Speak-Out-on-a-ball--47347249Any system is only as good as the information flowing through it. The natural human tendency (especially for developers) is to only communicate when it seems important. Just like your marriage, your project needs good strong communication to be healthy. This seems so obvious, but I have watched this kill projects time and time again in my career.

JIRA specifically is a platform that is well suited to capture the story of a feature or item as it goes through its life-cycle. If used properly, you can come back years later to make a change and be up to speed in a few minutes. On some projects we have design, workflow, commits, and technical discussion going back ten years. Our detailed data predates most of the employees at the customer, providing a historical perspective that adds yet another layer of value.

Capturing this data takes a culture that requires effort to build and diligence to maintain. Ask each developer to comment on their progress on a regular basis, establish some guidelines. Have Project Leads check this as a regular part of their work process, build a culture of positive accountability. Find places to establish checks and balances that will protect these guidelines and give you the confidence they are occurring.

Outside the box

While just avoiding the issues identified above will make your project better, we have found a couple of creative things that are quite beneficial when working with our customers.

#1 Screenshots/Video

Media is powerful. We have recently started looking for screenshots on any resolved issue that is customer-facing. Having these available paid dividends immediately. Pictures are not only worth a thousand words, but more importantly they apply clear context to all the words you did write. Many of your customers are visual learners, use this to your advantage. Providing a screenshot or demonstration video beats a live demo every time.

#2 Feature based agile board

Sample Agile Board

A quick and easy way to make a customer understand you value their priorities is a custom filter and agile board in JIRA. Simply add a label or a component, customize a filter, and create a board. Nothing will please decision makers more than seeing their high priority item flow through to completion. Giving that visual on the board can also eliminate those challenging schedule questions. They can see for themselves and begin to approximate completion. Do not underestimate the value of that last sentence. Giving customers the ability to answer their own questions quickly is powerful.

Conclusion

While not comprehensive, these ideas and concepts should help many who use JIRA or are considering the product. Staying on the good path takes effort but will pay off in improved communication with your team and your customer.

The Failure of Not Failing

bigstock-Failure-stamp-48396098We place such an emphasis on success in our culture and it is quite annoying to me. Perfection seems to be something we celebrate, and in some ways we should. What we do is idolize success without failure. For some reason we forget that failure is a necessary part of our development. I would venture to say no human being in history stood up and walked for the first time without falling. They did not speak their first words with perfect diction. Failure is a requirement of learning.People are often amused that one of my standard sequences of questions during an interview go something like this:

What is your greatest professional success or honor?

What is your greatest personal achievement?

What is your biggest professional failure?

What did you learn?

If you are comfortable, what is your most profound personal failure?

What did you learn?

I believe that these are important questions. I do not believe you can be a good leader unless you have failed. Sometimes people fail spectacularly, I know I have. A good leader understands how to keep going in the darkness of adversity, especially when they caused it. Good leaders have learned how to pick themselves up and keep going with that sick feeling of failure in their gut. A good leader can encourage others when the outlook is bleak and nearly hopeless. Adversity and failure are a good source of wisdom.

Unfortunately our school systems perpetuate this myth of perfection. Some of the most inflexible, unproductive, and unteachable employees I have had were 4.0 students. This is not a criticism of those students! Top grades are a great achievement, and they should be proud. What it is not, is a badge that means they know more than everyone else. I would argue their education is still incomplete because they have not failed. They do not know what inner strength they might have because they have not faced that embarrassment. I would not go so far as to demean those students, or not have class rankings, but we need to have it in context.

As a society we need to stop attaching a stigma to failure, especially for young people. As a young person grows, failing a class or getting fired from a job can be a learning experience more powerful than any classroom lesson. The world, especially the business community, is a complex place. You have to be dynamic and move fast. We need to equip our youth with the ability to recognize failure now and learn from it, not hide it. We need to celebrate that question above, “What did you learn?”.

Personally I value teachability and self-awareness along with intellect. Some of the smartest people I have known were some of the least teachable. The wisest and most productive people I have known were simply humble and teachable.

Our political system and sensational media also drive this perfection myth. How many candidates in the last presidential election were eliminated because of some scandal or failure. Some of these were real and some were trumped up but the dialog was the same. Can the candidate recover from this? Are they stained beyond hope?

This was infuriating to me. I want people in Washington who have had to recover from failure. Our country is failing and we need these people to show us the way. Continuing to send spoiled, silver spoon lawyers that know nothing of personal adversity will not change the result. We do not need perfection in Washington, we need reality.

I would encourage parents to incorporate discussion of failure into their dialog with their kids. I would encourage all managers to add it to their dialog with their employees. I would encourage voters to add it to their criteria in assessing candidates.

Failure is not only an option, but a necessity to be a valuable member of our society. Can we please start recognizing this?

Cloud Computing & Business

bigstock-touch-pad-concept-31114436Over the last few years there has been a raging debate over the intelligence of cloud computing for consumers. With the success of Dropbox, Drive, et al, we see the tide has turned. Consumers are increasingly happy being seamlessly (mostly) connected to their data across their computing platforms (pc, laptop, tablet and smartphone). The current running debate is about policy, and every so often you run across the conspiracy theorist (partially correct) that avoids Google’s knowledge of his existence. The overwhelming conclusion of this debate is that convenience trumps fear. If you look at the behavior of Facebook, Twitter and other social networks you can see them steadily pushing people away from privacy, and it is working. Consumers are comfortable with their personal details hanging out there for the world to see.

What about in business? That is a whole different animal, isn’t it? I would argue that just like trends in the midwest trail the coast by a predictable period of time, business adoption of technology compared to consumers follows the same pattern. Lets look at the rough era of proliferation of certain technologies in consumer electronics vs. business for a moment:

In the 1980′s computers became comfortable for consumers. I lump the Ataris, Commodores, Amigas and others in the category of computers. By 1990, computer penetration into the consumer consciousness was complete. By contrast it was not until the mid-1990′s that businesses automatically assumed each employee needed to have a computer in order to be productive. That pushes the acceptance gap at roughly 5 years. When it comes to mobile phones the adoption has been almost simultaneous. This is an anomaly that I think is easily explained by the critical nature of phone communication on business.

When you jump ahead to smartphones the gap is once again present. Smartphones became the norm for consumers with the proliferation of Android and IOS. Business is just now deciding that employees need smartphones to be productive. Previously they were optional and only for certain worker classes. I personally believe that the lack of dumbphone options on the market is driving business adoption, not some recognition of the value of smartphones.

Nexus 7You can also see the same pattern developing with tablets. As of this year most families assume they need a tablet as their next logical computing purchase. They are a hot commodity. Business is just now dipping their toes in the water, not certain that their applications can be ported to effectively work on them. Over the next couple of years I think we will see a rapid acceleration of the adoption of tablets in the workplace. Ultimately I think they will supplant traditional pc’s completely for some classes of workers.

These brings me back to the issue of the cloud. I hear a standard set of objections when considering cloud computing for business applications. They typically are:

  1. Security (Data, Intellectual Property, etc.)
  2. Cost
  3. Bandwidth

I believe that the widespread adoption of tablets and the required cloud computing platform will happen. It is an unavoidable tsunami coming that will obliterate some businesses while others surf to new heights. Here are my thoughts on each of those objections.

Security

lockI would boil it down to this simple argument. your current servers are on a network that is connected to the internet. There are routers and firewalls between your data and the unwashed, but it is still connected. If your employees can browse espn.com, then nod yes. In a cloud service like Google Apps for Business you are also behind routers and firewalls. The question is, who will do a better job protecting you, your IT guy that you do not pay enough, or Google’s highly paid engineering teams dedicated to internet security. While your guy is chasing that cabling problem on the second floor or rebooting the CEO’s laptop, Google’s team is identifying the latest threat and deploying adjustments in their system to counteract them. Who will you bet on?

Cost

dollarIf you are spending what you should on servers, data centers, backup solutions, UPS systems, etc. then a solid cloud option should be a wash in terms of cost. If you are doing a cheap, risky job on your IT see my previous point on security. You need to belly up to the bar and pay the bill. Your business and your customer’s data is worth it.

The cost of storage in the cloud is dropping steadily. There are even solutions that only charge you when you access your data. This is a powerful tool for companies that archive many years of large data. I often hear executives lament that the only time they need it is in a legal dispute and often then backups are bad and the data is lost anyway.

Bandwidth

cloudThis is a valid concern in the old model. Many people hang onto the idea that their data has to be present in the same building for it to be safe. This is simply the wrong way to view it. Cloud computing works when your primary storage is cloud-based and backups reside locally. When you pull files as you need them instead of massively syncing files to the cloud your bandwidth requirements drop dramatically. Sure, once a day/week/month pull a backup overnight but this is far different than trying to live sync files to the cloud as a backup. Drive syncing is a horrific drain on the network and completely unnecessary.

There are other arguments as well. Some I have touched on in other articles. The cost of computing hardware actually drops when you go to a cloud-based architecture. Tablets and Chromebooks are inexpensive and fully capable of performing most tasks in business. Sure CAD, extensive graphics work, and modeling are all the domain of workstations. But email, browsing, business documents, business graphics, are all readily executed in the cloud. I believe this wave is coming and I am seeing the benefit of moving Oasis Digital to the cloud two years ago. The benefits have far outweighed the negatives, hands down. I think that will be the case for most businesses in the coming years. The PC is dead, long live the cloud!

A Year With Chromebooks

ChromebookA year ago we purchased the Samsung $249 Chromebook from Amazon. For a long time it was easily the best selling laptop on Amazon. Then in February Google released the Chromebook Pixel. A video and shots of the device were leaked widely on the web and panned universally as foolishness that would never become reality. Google had in fact been hard at work building this device to their specifications with a clear focus on existing in the same class as the Macbook Pro. One of their goals was to establish that a cloud based computer could be used as a primary computing device. They believed that this would give Chromebooks the needed panache to convince average users to try.

I will start be saying that the fact that I am still using the Pixel as my primary computing device, I literally handed down my Zenbook to a new employee, as a clear sign of success. I have always been a power user of a pc, pushing applications places to perform tasks they might not have been intended to. I would like to take the time and run through my top 5 dislikes of the Pixel first and then hit my top 5 likes of the device.

Dislikes

bigstock-Battery-Indicator-182032521. Battery Life

It is a real shame that Intel did not have their act together yet for the launch of this device. The Haswell platform has substantially better battery impact than the Ivy Bridge processors. That said the device would have been greatly imporved if it was 1-2 mm thicker and had another 1-2 hours of battery life. I regularly get about 5 hours use on a full charge if I pay attention to the screen settings. If I do not it is not unusual to burn through my battery in less than 4 hours. This is a shame.

The lack of battery life also has required that I take my tablet with me in more situations than I normally would have. On a long plane flight out to San Francisco my Pixel has no chance, this means I am currently writing this article on my tablet en route to Charlotte. Again, a shame as I would prefer to travel with one less device.

2. USB 2.0

The lack of USB 3.0 ports is baffling and to this day I have not heard a good explanation. I guess it is possible they were trying not to tax the battery any more than it already was. It is also possible they were just saving money. I find the former far more likely with the latter just not jiving with the rest of the premium feel of the device.

3. HDMI

With the only port being a mini display port it was a little frustrating at first accumulating the needed adapters to make this device useful in all situations. Once I acquired the HDMI, Display Port, and VGA adapters needed I was fine but my bag was a little more cluttered.

4. Serial Number

Yes, I am really reaching to find 5 things I do not like about the device. The support for the Pixel is fantastic and they have successfully helped me navigate a couple of situations well. Sometimes they just remind me of something I should already know but at least they do not send me a snarky video of how to Google it! All that said a precursor to getting support is entering the serial number. This number is etched into the bottom of the device and is so small I have to get my jeweler’s monocle out to read it. The number is also not to be found in the OS anywhere. This makes getting support when hooked up to my monitor, power supply, and peripherals quite a pain.

5. Accessories

Other than the battery life my greatest frustration has been the lack of high end accessories to go with this premium device. The pat answer of using the accessories designed for the MBP is just not acceptable. The unusual, and fantastic, screen aspect ratio gives the unit a different footprint making most aftermarket cases awkward at best.

Then there was the debacle of spare power supplies. Obviously the person who managed this aspect of the supply chain has spent their whole career chained to a desk. Everyone I know that is a power user and relies on their laptop as the primary computing device immediately buys at least 1 if not 2 spare power supplies. It was fully 3 months before they were available and then you could only purchase 1. This was utterly ridiculous and unacceptable. Hopefully the person responsible has been beat around the head and shoulders by enough coworkers armed with Nerf swords that this will not happen again!

Likes

Chrome1. Chrome OS

We have seen this pattern before with Google and they are up to it a third time. Android started out as a mockery of a phone. I used the original TMobile G1 from launch and suffered through Androids early growing pains. I was mocked by all my iPhone toting friends. Failure was assured, Apple could never be overtaken…well we now argue which Android phone is the best in the world and often Apple is not discussed. The same process occurre with tablets. Earlier this year Android tablets overtook Apple in this segment as well and I do not expect we will be looking back.

Chrome OS is growing up right before our eyes. New, exciting features are coming weekly now and the bridge for Chrome users on Windows/OSX is completely in place. If you use Chrome as your browser (most do) you will find them in place when you log into a Chromebook. Education is Apple’s mainstay and saved their mindshare in the 90s when their corporate influence vanished. If you look at the 25-45 year olds that primarily use Apple products it is that exposure in schools that deeply impacted them. What is happening right now, this year, should shake Apple to the core (sorry for the pun). Education is not just moving to Google but flocking to Google in droves. Chromebooks are far superior to iPads for education and Apple cannot afford to make the Air competitive. Google is winning the next generation.

All that aside I really enjoy using the Chrome OS each day. It is fast, predictable and allows me to move quickly and seamlessly between tasks. There are a few things I run into that I wish were present but creating content, interacting with my world, and working offline are all very effective. The big hole in the app support space is screen-sharing and collaboration tools that have business features instead of what Hangouts has to offer. I realize this will be fixed in the future but most of the big tools do not work with Chrome OS.

2. Keyboard/Trackpad

In a word it is fantastic. I am spoiled and will have a hard time adjusting to anything else. I have arthritis in my hands and even after a long day I can type smoothly and pain free on the keyboard. The key travel is perfect and superior to the Apple keyboard I use with it on my desk. The backlighting is so good I wonder ifit could be turned it down and save a bit of battery.

The trackpad is equally impressive. The surface is particularly pleasant, I will avoid analogies here, and the size is perfect. Scrolling and gestures are also excellent, especially when compared to Windows 8 efforts. I find this rather humorous because Google got it right on a device where it is a secondary feature and Microsoft could not get their cornerstone feature correct. That really says a lot to me.

3. Screen

Much has been written about the screen. Suffice it to say you cannot purchase an external monitor for less than $2000 that looks this good or has this kind of clarity. The screen is as good as it gets. I love it and it will be hard to move to anything lesser in the future.

I also have really come to love the aspect ratio. I have found it useful in much the same way I use my 10″ Sony tablet in portrait mode. It is perfect for reading and absorbing content. I might feel differently if my most common use was video but this format is absolutely outstanding for me. The last time I used a laptop screen this way was my Thinkpad with the 1600×1200 screen.

Drive4. 1 TB of storage

This may be the most underrated feature of the Pixel. It took me a few weeks to get all of my data into the cloud but I no longer worry about losing information. It seems counter-intuitive but I prefer to have my primary storage be in the cloud with local being my backup. By having my data there it is instantly available from my tablet, phone, or any other device I log into. It is hard to describe how easy it makes being productive. I am writing this article and I know that as soon as I land it will be synced to the cloud and I will be able to review on my phone or Pixel before I board my next plane.

I am also particularly pleased with my backup strategy. Props here to Spanning Backup for an outstanding service, I highly recommend it. By using another cloud service I have my data in a separate cloud network and I have the ability to download it in bulk. I do this to an external drive at my office. I will also periodically copy it to another drive and stick it in a safety deposit box. Without much effort or cost at all I have 4 copies of my data. This is all possible because of the TB of storage received with the Pixel.

5. It (almost) always works

BSOD is not a feature of Chrome OS. ‘nough said. There are hiccups now and again, see #8 for why this is not an issue.

6. Build Quality

I am really impressed with the quality of the device. This has been covered elsewhere ad nauseum so I will not elaborate but it is really outstanding. There were those that were concerned with the hinge when it was first released. I can honestly say that I see no change with constant daily use, it is well made.

7. Speakers

The sound system this device has even impressed my teenage son. It easily fills a room with clear intelligible sound. It literally is the perfect device to use as an ad hoc conference phone. The shame is that none of the major meeting platforms support it. It certainly makes listening to media a pleasure.

8. Boot/Reboot speed

The device is so fast I have found myself not caring if I shut it down or suspend. This is a major shift from expectations on other platforms. I would suggest that the device really does not need both options, it really is that fast.

9. OK, I will stop

Yes, I love it! My adventure into a post-Windows world has been a rousing success and I do not expect to look back. I truly believe that those who can adapt and move this direction with the industry will thrive.

Three Critical Principles Of Developing Enterprise Software

Understanding, Workflow, Delivery. These three concepts sound trite, soundbites in technology for decades (substitute Execution for Workflow during the 1990s). It is hard to overstate the value of these key ideas and it is even harder to do a good job in making them a natural part of your team’s effort. They have many enemies:

  • Tyranny of the urgent

  • Laziness

  • Personal design preferences

  • Ego

  • Meetings

  • Distraction

  • Conflict

All of these can destroy an otherwise productive team. It is a leader’s job to manage these and protect the team from them. It is the individual team member’s responsibility to manage their own work and remove as much of this as possible. It takes everyone working diligently to work smarter for a project to succeed.

Understanding

All too often we jump into projects thinking we understand what needs to happen only to find that we did not. At that point work has to be redone, time is lost and the customer is frustrated. Disciplining your team to dig in and fully understand a set of customer requirements is important. Please note that written requirements accompany but do not replace the understanding required to provide a solution. The written requirements are a tool to use but cannot replace the cognitive process of understanding.

When you start a new project, there is a natural limit because a customer will not tolerate an extended delay at the beginning of a project. You cannot charge blindly across the customer objection but you absolutely need to understand the problem domain at a deep level. This is a fine line that is difficult to walk. Using tools in a way that trap critical information is very important to achieving this balance. Have the customer provide videos of key staff performing their work function or using their current application. Customers will want you to be present and witness this, but resist. We have found it is critical to capture this in a way it can be watched over and over again by team members. Through this process real understanding can occur. Without it your team will find it difficult to remember a meeting six months prior and have trouble decoding the typed up notes neatly stored on their computers.

It can also be helpful to listen to customers talk about their vision for the project. Again, do not do this in a meeting but obtain a recording of this. New team members can hit the ground running after reviewing such valuable materials and the tools are readily available. This is not to say you cannot have meetings that supplement this information but all too often a meeting is all that occurs. It is commonplace for notes to be sparse from these meetings and our memories are notoriously faulty.

Once you capture this knowledge it is important to translate this into a set of requirements. Sometimes customers will give you these, but most of the time they are too busy and this falls on you. Follow through and process this into a detailed list of features that can be pushed into your workflow.

Workflow

There are very few times that enterprise software will be simple, and any complex project can be derailed without a clear process for execution. There can be variations by organization, but I will speak generally of the process we use. Below you see a graphical representation of our workflow.

workflow-enterprise

It is important for the project lead or architect to lay out a particular piece of work. Once it is ready it should go into a bucket where all the developers on a project can see the work that is staged. We often place these into milestones, sometimes referred to as sprints. Developers pick up the work and push the item through development, peer review, internal testing, and QA Testing.

The enemies I listed above mostly rear their heads to attack your workflow. There are always reasons that make sense at the time that lead to workflow destruction. A leader is responsible for guarding the gate. If any of these are allowed into the team and allowed to take root, there is a high likelihood of project cancellation, termination, and failure. Pushing items efficiently through a workflow allow for the most important part of a software project to occur: delivery.

Delivery

From a customer’s perspective you have not really done much until they have software in their hands. Use the previous two efforts to both understand the core features and aggressively push them through your workflow. Use your understanding to plot the quickest path to putting software in customer hands for comment, feedback, and tactile success. Getting a project to that point is critical to its continued success. Completion of some limited functionality is far more valuable to your customer than partial completion of 100s of features. Shipping is golden, a lack of shipment is a recipe for disaster.