CSS grid with Angular and CLI – the time is now

Today, early December 2017, is the time to begin using CSS grid for layout in Angular applications, even if they must support Internet Explorer. We can stop enduring the costs and delays of old “float” based CSS layout, and get better results with less work, using CSS Grid – even with Internet Explorer support requirements – with caveats described below.

Take a look at a running example on the browser of your choice, including both modern browsers and IE11.

https://oasisdigital.github.io/cli-css-grid-demo/

Background

If you’re not familiar with CSS grid, the best source is Rachel Andrew, the global guru of CSS grid. Either read and all of her Grid content, or peruse the links below (thanks mostly to Bill Odom, our early CSS Grid cheerleader, for gathering these). Now is a good time to read and watch, I’ll wait.

Welcome back, CSS Grid fan. Of course the big problem with Grid today is that while support is excellent among current browsers, many users (especially paying, enterprise users) are still wallowing in Internet Explorer. IE has basic support for CSS Grid, but the support is for an older spec which has both fewer features and different syntax. The syntax is irritatingly different enough that manually maintaining both is prone to error.

Fortunately, the incredible Autoprefixer does a very good job, in version 7, of papering over the syntactic differences. In many cases the benefits of Grid can be obtained even without the newer semantics.

Yak shave

Unfortunately, Angular CLI (as of version 1.6.1, as I write this) uses Autoprefixer 6, and exposes no way to adjust Autoprefixer settings. This CLI issue tracker has many open issues, and it appears the team is closely focused on core application bundling and ergonomic considerations, so it’s hard to predict when CLI team attention could turn to issues like this.

Yet here at Oasis Digital, we are ready to use Grid today, and our customers are ready to deploy software today. Therefore, a series of workarounds is in order. To see them in action and in detail, visit this demo repository:

https://github.com/OasisDigital/cli-css-grid-demo

…and for an explanation, read on.

Upgrading Autoprefixer

To use version 7 in an Angular CLI app today, a way must be found to override the Autoprefixer dependency. The traditional answer to override dependencies and settings in CLI is to “eject” – but that is a big leap, not easily reversed, and not recommended. An application based on ejected CLI presents a greater maintenance burden for developers. Instead, we generally recommend sticking with CLI but applying whatever patches are needed at run time to get the right behavior.

Unfortunately as of late 2017, it is still unduly difficult to override dependencies with NPM; searches looking for the way to do it, lead in circles toward old NPM versions. Happily, Yarn can do it quite easily, as a first-class feature. Switch to Yarn, then add a section like this to the package.json file:

"resolutions": {
  "autoprefixer": "^7.2.3"
}

Turn on grid support

Next, Angular CLI does not yet provide a way to pass options to Autoprefixer, and using Grid requires turning the support on. To work past this, the venerable approach of “monkey patch in a postinstall script” solves the problem easily. The script content is essentially just:

sed -i.bak -e 's/autoprefixer()/autoprefixer({grid:true})/' \
 node_modules/@angular/cli/models/webpack-configs/styles.js

This reaches into the relevant file inside the installed CLI code, and edits it in place. I think of this as a rough but necessary hack, to deliver value today, reaching ahead to the future when the tools will make the hack unnecessary.

Fortunately, between these two workarounds there are just a few lines of edit needed in a project. Study the repository above (especially the second commit in the commit history) to see the exact changes.

Conclusion and caveats

Of course there are caveats here, explained in depth by the Rachel Andrew page I linked above. The situation is not quite as severe as that page suggests though, because of what Autoprefixer does. With this setup, you can use today’s Grid syntax, but the subset of Grid semantics supported by IE. This means:

  • Use modern grid-column definitions etc., no need for the older “span” concept.
  • No “flow” in to grid cells – assign grid locations manually. Fortunately, for application layout Flow manual assignment is common anyway.
  • No “gaps” – leave an empty track instead. Easily done.
  • No grid-template-areas.
  • As always, remember to test on IE.

While these caveats are a bit frustrating (especially the lack of grid-template-area), this use of Grid is still an enormous improvement over legacy CSS approaches for many (or most) application screen layouts. With this approach, I see no further reason to wait to start using Grid broadly in Angular applications.

Future work

If the lack of grid-template-areas proves too frustrating, I may look at a similar approach to squeeze in support for postcss-grid-kiss; it provides syntax far beyond that offered by grid-template-areas, and also provides more semantics on IE through use of greater CSS contortions.

 

Angular routing – advice for real applications

There are plenty of examples and documentation about the Angular router, but these things sometimes leave important questions unaddressed.  Documentation often intentionally demures from questions like “what is the best way to use this?”. Even my own previous post briefly reintroducing the router does the same.

Here are our recommendations from extensive use (at Oasis Digital, in classes and complex customer applications), with my specific take on contentious points, on that category of Routing question. How can the built-in capabilities of Angular, including the router, be used with maximum leverage? How can an application be written “with the grain” of Angular to produce the greatest value with the least code? How can the router be used to provide a good user experience and functionality?

URL/route for navigational state

The standard use of intra-application URLs is to represent and control navigational state. Navigational state means “where” the user is in the application. Which screen; which entity; what they are working on; what they are looking at. This type of state so strongly belongs in the URL that (in a polished, important application) it should always be managed via the router -even if some other state mechanism is being used to manage other aspects of application state.

Pop-ups and auxiliary routes

The Angular router has an auxiliary route feature, uncommon among other routers for other frameworks. This feature has various uses, particularly for (unusual) applications with more than one section of the screen that might be navigated separately. But it also has a common use: if an application has a pop-up/popover/dialogue of some kind (for example, a list of users in which editing a user happens on the same screen), the state of whether a pop-up is currently visible should be represented as an auxiliary route.

Resist the temptation to have a pop-up work separately from the route, because that would mean that bookmarking or sharing a URL would not capture this aspect of the user’s navigational state.

Router state and form state

Sometimes a form is used for data entry; for these cases the state of the form (particularly if you’re using model driven/reactive forms) is a fine place to keep that interim data entry state.

But in other cases, a form is used for something like a faceted search. Search parameters can easily stray into navigational state. For example, if the user is currently searching a list of orders for a certain date range that mention a certain product, they could very reasonably want to navigate forward and back to that state, they could want to bookmark and share the URL, and have those search parameters come along.

In these cases, it is reasonable to mutually interconnect the router state and the state of a form. That sounds difficult, but requires just a few lines of code. The result can easily provide a near ideal user experience around searching, URLs, the back button, bookmarks, and so on.

Router state and ngrx/Store

Ngrx/Store users have some extra tools at their disposal around the router state. There is an optional add-on package which integrates router state into Store state, so that it can be managed via the same mechanisms (actions, reducers, effects, etc. An application of significant complexity, so much so that it needs Store, almost certainly also has significant navigational state, and should strongly consider integrating them together.

Don’t fear ugly URL parameters

In simple cases, a URL contains a flat name-value pair list of optional parameters, in which the contents of such strings are most typically just a single value, it is also acceptable to pack in many values in such a single parameter by encoding a broader swath of state as JSON. For example, consider a simple search of orders in the system for order management. It might have single search parameter, perhaps which matches a product description. The URL for the state of searching for such a description could look like:

/orders/search?productMatch=blue

But for a more complex search (for example think of a faceted search with 15 different fields by which the user could search for old orders), you may need more (bug-hiding) code to shuffle search parameters into and out of URL parameters. It is also acceptable, and sometimes more advisable, to encode all of the complex search parameters like so:

/orders/search?q=...

where … Represents a URL-encoded JSON object describing the search parameters

Such a URL is less straightforward to inspect by hand, but also less work to manipulate programmatically and easier to expand to encompass more parameters. Make the trade-off at the application level, as to whether this yields a better overall system.

Router security concerns

I’ve seen suggestions of route guards is a security mechanism; but it’s important to remember that the entire browser is a user agent, it is literally an agent of the user, not the agent of the developer or of the backend system. At best a browser application can avoid making security worse, but it doesn’t actually provide security. Never assume that route guards or other client-side mechanisms are providing any real security, rather think of these mechanisms as advisory security. Advisory security is UX/UI which makes it easier for the user to avoid wandering into a screen which will break because server-side security rules interfere with its operation.

But there is a new and interesting way that browser-based applications can get things wrong with security where the router is concerned. The entire route URL, which means all route segments, parameters, outlets, etc. is untrusted user input. It could accidentally or intentionally contain errant or malicious data. Make sure to treat route data as such, sanitizing it etc. as one would any other user input.

Matrix parameters

Although not used very widely, there is a URL pattern called a matrix parameter, in which each “segment” of the URL has its own parameters rather than just one single bucket of parameters for the entire URL. The Angular router supports this nicely, by using it you can sometimes conserve application code quite significantly while still providing a more ideal user experience around navigational state captured in the URL.

Route guards for data loading

Longtime Angular users who started with AngularJS often point back to the “route resolve” feature is a critical capability they’re looking for an Angular. The Resolve feature makes it possible to delay (or cancel/fail) loading of a route until the data needed to populate the screen for the route is ready.

I recommend using this feature with caution and sparingly. Often a better user experience can be achieved by proceeding directly to a route (for example, a customer history detail display), and then asynchronously loading various parts of the data which appear on that screen. While the screen painting can be a bit messier this way, the user will perceive that the screen started loading much more quickly than if loading is delayed until all data is available. Even if the difference is only a few hundred milliseconds, showing the user partial results is typically a better default.

Angular routing, a basic Q&A

At Angular Boot Camp, we thoroughly introduce and teach the Angular router – over the course of 3 days, spread out into relevant bits and pieces of other learning. Outside of class though, customers ask a straightforward question: What is the Angular Router, and why should I care?

To answer that, this post is a tidy re-introduction to routing in Angular. It is presented in Q&A form – there is little reason to reproduce the router documentation, so this is more like the average of many conversations.

What is routing?

Most concisely, in a web application routing means the relationship between the URL and the state of the application. State can mean a lot of things, but in this context it means “what screen the user is looking at” and “what specific entity/data the user is looking at on that screen”. For example, an application might have “/orders”in the URL when they are looking at a list of orders, or “/orders/12345” when they are looking at order number 12345.

Why use a router?

Routing is about translating between this concise string in a URL, and the rest of the machinery of an application, without coding that translation “by hand”. Developers sometimes ask why they need a thing called a router to do that, whether they might just instead inspect the “window.location” variable and make the application show the right thing. In a sense, the answer is yes – you could certainly do that. But it tends to get complex as an application grows, and if you do it ad hoc, your code won’t have as much in common with other application code. By using a router, you can write less code, and have a standard off-the-shelf solution to a problem that most applications need to solve.

Why care about the URL?

As you learn Angular, you can see how to use variables and ngIfs to make different data appear on the screen in response to user clicks. For example, you could have a variable “orderScreen” and some section of your template using ngIf to display only if orderScreen==true; then have a button which sets orderScreen=true. So you can easily see how to display different data based on what the user clicks, without caring about the URL.

But URLs (and by this I mean the part after the domain name) are the standard, well proven Web way of expressing “where” the user is. Users understand URLs, and users can copy and paste URLs in email, users can bookmark URLs. If your application has a specific URL to mean “order list screen”, a user could bookmark that and navigate directly to it when they like. Fundamentally, URLs are user-friendly.

Would it be easier to just write a separate “application” for my orders list screen? Could I avoid having to understand the router by making each screen a separate application?

I’ve seen applications, especially those adapted to fit inside an server-side system, which eschew the notion of “routing” between different screens and instead have an entirely separate application for each screen. This is possible, but inefficient. The browser ends up needlessly reloading much of the same JavaScript as the user navigates from one screen to another. With the router, the user will only need to load the new, different code for the next screen as they navigate.

Similarly, the router implements “lazy loading”, so just like the idea of totally separate applications, with the router, a user doesn’t have to wait for their browser to load the “order screen” JavaScript until they are ready to use that screen.

The router provides the ideal mix of efficiency in development, efficiency and deployment.

How do I get started with the Angular router?

As you create a new application with Angular CLI, there is a routing option which sets up the basic structure of routing for you. Unfortunately as of late 2017, you still need to manually code up specific routes, which you can do by following the Angular router documentation or various tutorials online (or of course, learn in our class). I expect a future evolution of the CLI will automate more of the router configuration process.

When should I get started with the Angular router?

A few years ago, I used to recommend waiting for routing until you really need it, until your application has more than one “screen”. But now it seems more advisable to simply follow the standard patterns for routing from the very beginning. As you create your first screen in an Angular application, go ahead and implement that screen in a module, and use router lazy loading the load that one module. This seems like extra structure, but will save you from having to rearrange your application code when a second “screen” is inevitably needed down the road. This is also exactly the path we teach in Angular Boot Camp.

What about that idea of routing to a specific (for example) order, rather than to the list of orders?

The idea of routing to a specific individual entity in your application problem domain, use a route parameter. A route parameter is simply a section of a route which can be filled in at runtime with a string. For example, “/orders/12345” suggests a routing set up where the second segment of the route (12345” is a parameter. This is easily configured in your Angular routing configuration, you can see the documentation for the exact syntax.

The more interesting part of a route parameter is consuming that parameters, being aware of it from inside application code. These route parameters arrive at your application component as an observable value. You’ll need to use a small amount of RxJS code to trigger loading of the appropriate data based on that route parameter. This sounds confusing and complex, but you can find examples online would show it is often just a few lines of code.

How do I link to a route?

You can link to a Angular route in an ordinary anchor element (“<A….”) in a component template. You do this using the router link directive (attribute). The documentation shows the exact syntax, but the important thing here is simply that you link to a route within the same application, with the syntax only mildly different from linking to any other page on the Internet.

Things get slightly more complex when you want to link to a specific entity (going back to our example, “/orders/12345”). To do this you use something called a route parameter array, in which the application code snippet has an array with these two parts of the route (orders, and 12345), which then get assembled automatically by angular routing into a working route link.

Of course in real application, users often click a button to do something rather than follow an ordinary web link; you can accommodate this with routing either by styling the link to look like a button (quite easy with bootstrap, for example) or with a line of code in a click handler to ask the router to navigate to a link.

So my links could be navigation in a sidebar or top bar, right?

Yes, the most common use for router links is in a navigation bar of some kind.

In this context, it also makes sense to visually mark which route link is currently “active”. To make it obvious to the user which part of the application they have already navigated to. The Angular router also makes this quite easy with an attribute “routerLinkActive”.

Is there anything else to know about routing?

There is an abundance of important capabilities in the router beyond this quick Q&A introduction. Past this introduction though, it starts to get a little bit more philosophical, and make sense to study after you are already experienced with basic use of the Angular router.  I will follow up with another post on some of these other routing thoughts.

 

 

Software Demonstration and Project Status: Use Video

At Oasis Digital, custom software projects work at various cadences: weekly, biweekly, or sometimes in variable-length cycles. Regardless, at each interval or milestone it’s important to deliver a comprehensive demonstration and status update for our project customer.

Live demonstrations considered harmful

Unfortunately, the most obvious way to deliver demos and status updates does not work very well:

  • Perform a live, high-stakes demo – Murphy’s Law applies. Systems break during a live demo.
  • Freshly, the first time, making it up as you go along.
  • Think about project status only when asked.
  • Seen only by stakeholders who are able to attend the meeting – often a small subset of the people who care about the demonstration and status

It seems silly to even describe these things, but I’ve seen this poor approach as standard across much of the software development world.

Effective software demonstration and project status delivery

We have refined a much better way to deliver software demonstrations and project status updates. The short answer is, “make a video”. The long answer is to make a comprehensive demonstration and project status update video, deliver it to all interested stakeholders, then have a meeting to discuss the demonstration and states. This results in an easier, deeper, and more thoughtful meeting and also serves stakeholders who can’t attend.

Every demo/status video serves a number of purposes and audiences; so it’s important to cover topics of interest to all kinds of stakeholders, not only the stakeholders most able to attend. At the same time, we don’t recommend creating multiple videos for multiple audiences; that is an unsustainable pace of content production, it takes too much time away from the core work of creating quality software.

Make one medium-length video per cycle (week/biweekly/whatever) to address:

  • Demonstration
  • Project progress summary
  • Upcoming work
  • Key open questions
  • Interesting or important technical details

In this way, each video is of value to both “local” stakeholders (the specific customer team managing the project from day to day) and broader stakeholders across a customer organization.

Next, the nitty-gritty of what goes in to a such a video and how to make it. The agenda should go roughly in this order.

Introduction / Title Slide

Files (including video files_ tend to be misclassified, mislabeled, and misplaced. Someone might open up your demo/status video and not know anything about what’s inside. Therefore, always start with a title slide. That slide should include:

  • Name of the project
  • Name and logo of the customer organization the project is for
  • Date (sometimes just month and year, for slower-paced projects)
  • Name and title of the person making this video (speaking)
  • Name, URL, and logo of the company working on the software (for us, “Oasis Digital”)

While the title slide is visible, briefly introduce yourself. You have only a few seconds of viewer attention; the slide and your introduction should last 10 seconds or at most 15, before you cut to the next section.

Still video is a waste of bandwidth, and drives viewers away. Never let the video stay still while you talk for more than a few seconds.

Demonstration

After that brief introduction, jump right into the demonstration. If you learn only one thing about effective demonstrations, here it is:

Get to the payoff fast.

Don’t wander through a long buildup in which only the most dedicated viewer can reach the important part. Show the payoff, the most important bit, within the first few minutes. Then, go back and explain the rest of the story to give a comprehensive demonstration of use cases.

Your demonstration should bring the viewer through one or more use cases relevant to the work underway. Through these use cases, remind the viewer of the overall purpose and functionality of the software project, and point out the new and changes parts, showing progress.

Demonstrations tend to go wrong, or to waste a lot of time, by default. To produce a quality demonstration:

Practice.

Yes, practice. Jot down a terse outline of what you plan to demonstrate, and practice it a couple of times (with the video recorder running) to get familiar with exactly what will happen. If you see anything urgent to fix while making these practice attempts, you might stop and fix it right then. Then once your practice demo goes well, record the real demo.

In a demonstration of a user interface, text and UI elements be readable. We get the best results by sizing the software and recording a “stage” of 1280×720 pixels. A video that size can easily be played back in a non-full-screen window on a typical computer. If your software under demonstration can’t be used at the small window size (i.e. screens that really only work at 1920 resolution), make sure to boost font sizes.

(Some stakeholders, including quite important ones, might only have an opportunity to watch your demonstration video on their cell phone! Think about font and other element sizes accordingly.)

Lastly, create a demonstration you can be proud of. If your demonstration went badly, discard that recording and do it over. If you have been keeping your demonstrations tight, it won’t cost much time if you occasionally have to discard and try again.  If your demonstration is so long that starting over is unthinkable, make shorter demonstrations more often.

Project status and management update

After demonstrating progress on the software, provide an update on the project. We heartily recommend the following order:

  1. Review at what has been done since the last update; positive progress
  2. Preview at what is coming up next; anticipated progress
  3. Discuss upcoming key questions or issues that could delay or prevent progress

Point 1 is especially important and easily overlooked. We have had projects which were objectively going extremely well: delivering a pile of valuable functionality every week for years on end. But looking back, it’s easy to get in a meeting rut – the tone of a project can be ruined by an inadvertent meeting focus on only what is going wrong. Therefore, before discussing what is coming up in what might go wrong, always briefly summarize what has gone well.

The details of how to show status and upcoming work vary by your methodology and toolset. We most often use Jira, and to talk about these things by scrolling, clicking, and talking about an Agile Board in Jira, often supplemented by a Dashboard. You can do the same with other software, or even with a manual project management system.

Obstacles and questions

Having shown visible progress in the demonstration then talked about project status, you now have the viewers’ attention to deal with challenges. Most likely any obstacles or questions are connected to issues in your project management tracking system; so click back through the relevant ones and discuss these things. Make sure to show the relevant part of the software and the relevant bits in the project management software. (Reminder – never more than a few seconds of still video with just a person talking.)

We have found that our recap of obstacles and questions on video, can be very helpful to our customers representatives. They can show the video to other people in their organization who might be able to help with the obstacle. They can listen as well as read – some people enjoyed listening more than reading. They can arrive for a live meeting, already having thought about the questions and ready to answer.

Technical

The last major section of a demo/status video should dig into any interesting or important technical aspects. Here is the chance to show an IDE or source control tool instead of just the running software or Jira board. Most likely the technical bits worth discussing will concern either recently completed features or features coming up shortly, but sometimes a broader topic might warrant attention.

In our experience, digging into the important technical details can also support rapport and credibility with more kinds of stakeholders. Every organization contains a mix of people most responsive to project management, and others most responsive to technical depth.

Closing

As your video ends, flip back to the title slide and thank the viewer for their attention. As hard as you may have worked (more than the length of the resulting video, sometimes much more), your viewer has also dedicated their limited time to watch. Thank them.

Video and audio production tips

Surprisingly, often the most important aspect of video production is audio. You need a quiet room and a decent quality microphone. The former can be hard to achieve in a busy crowded workspace, but it’s worth the effort. Hide in a conference room. Get a coworker to stand guard at the door.

An amply good microphone costs well under $100. We’ve had good results with various types of headsets (but read more about that later), with Blue Snowball microphones, and with a popular Audio Technica model. All of these are quite inexpensive. Any of them are vastly better than trying to use a laptop’s built in microphone.

Next, screen video. You’ll need appropriate screen video recording software, and you will need to master its configuration. We recommend:

  • ScreenFlow, on OSX
  • OBS, on Windows

Video is about more than just the screen though. If you’ve made it this far into this post, you are ready for perhaps the most important advice of all:

Show your face

A demo/status video is not only about information delivery, it is also about personal connection. Humans are hardwired to connect with other humans while looking at their face. Therefore your face should be visible in the video. Both of the software packages above can easily show your face in a corner of the screen. Do so.  (Back to the headset idea – a headset can provide excellent audio pick up, but then you will be wearing a headset in the video. Therefore the headset is not the best solution for this use.)

Video of your face means you need a camera. Most laptops have an amply good camera built-in (but sit your laptop on a stack of books or something handy – so that the laptop camera is not looking up your nose!). Or add an external webcam (< $100) atop an external monitor for better results.

Speaking of cameras, cameras detect light. Rearrange the lighting in your space (or add a $30 lamp) to get some light on the front of your face during your video recording. Your eyes should not be in shadow.

If your recording software supports it (both of the above mentioned packages do), add a “bug”, a term of art for a partially transparent logo in the corner of the screen. For example, if you decide to put your face in the upper right, then the lower left of the screen could contain your company logo at 50% transparency. A video is a branding opportunity in addition to an information communication opportunity.

Finally, reread the advice earlier in the demonstration section, about font and screen recording sizes. Then read them again. 🙂

Feedback wanted

We have worked out the advice here over years of various attempts to communicate demonstration and status information well. But we surely have much more to learn, and appreciate any feedback readers send. Thanks for getting this far, and good luck in your demonstrations and meetings.

Product Development Launch – Default Software and Practices Stack

Context

Here at Oasis Digital, some of our projects are (approximately) “green field” product development launches. The scope of such a project typically includes some CRUD-like features, but also a complex-behavior feature or two. The effort typically lasts a few weeks or at most a few months, after which work is transitioned to customer developers (or occasionally to longer-term ongoing work here).

During a product development launch, we typically demonstrate:

  • Key goals are around user experience, UI development, etc
  • Key use cases of a system
  • Working software, sufficiently deployable for demonstrations
  • Feasibility and suitability of a technology stack, client and server side

Importantly though, during such a launch effort the long-term viability of the underlying customer vision is not yet fixed nor proven. Rather, a product launch refines the vision and proves the potential value.

Executing a product launch

For the reasons above, it is vital that we execute a product development launch expediently. The process is typically something like so.

  • Understand the vision and goals
  • Collaboratively define some key use cases, and key user experiences
  • Defer as much complexity as possible, outside of these key use cases; don’t let the development launch turn in to just a planning effort
  • Choose off-the-shelf tooling to facilitate quick implementation
  • Define key screen flows for the use cases
  • Consider what data appears on each screen (report, integration, etc.), and the flow of data through the system
  • Define an initial “schema of the system”, iteratively through the launch effort
  • Work on an iterative cadence so that we can get through at least several significant iterations during the short project duration

All of that is just context though; what I really want to talk about here is our default software stack for launching a fresh new project. These are just defaults; they often vary by the needs of a specific project, customer, deployment context, etc.

Client / UI

As of 2017, we generally default to a single page web application powered by Angular. While we also work in React and other tools, Angular is where we have the greatest shared experience (from extensive development work, as well as from teaching Angular Boot Camp) and therefore the greatest immediate collaborative productivity.

Angular is also the technology area where we innovate most. We use it for many projects, we train on it, we follow its development closely, we participate in open source. We attend and sponsor conferences. We are connected with the Angular community.

At the same time, customers coming to us for a product launch are typically most interested in seeing a working user interface that demonstrates their vision. Therefore the greatest share of our work in a product development launch is in the user interface.

Server

Because typically our time is focused primarily on user interface/client-side work, it is important to have a set of highly effective tools with which we can execute well-understood server-side APIs very quickly. Therefore, we default to:

  • Java
  • Spring Boot
  • Spring JPA / HIbernate
  • Various other ancillary related libraries and tools
  • A transaction scripting approach for the handful of complex use cases in a bunch effort

These tools are, perhaps to a 2017 eye, somewhat boring. But they are boring because they are well proven, they work. They very rarely yield, within the scope of initial development, any significant obstacles to delivery. That makes them very well suited for a short-duration effort.

Because these tools are so well proven, and because they permit a mostly declarative implementation approach, the resulting small code base warrant little automated testing at the beginning.  We don’t need tests to show that this stack can correctly implement a RESTful API; if it had any trouble doing so, we would replace the stack, not nitpick it with tests.

(While Java is the typical default, we also frequently use Node and related libraries instead; there is a trade-off here between less mature tools, versus the payoff of using more similar technology between client and server code.)

GraphQL

Sometimes things are not quite as boring as they seem though. If the data to be fetched is complex, we typically pick up GraphQL to slash the code quantity and development time for complex data fetching. Data volumes are usually modest during a short-term launch effort, so a straightforward lazy fetching approach via GraphQL resolvers (which go by another name in some implementations) does the job with little effort. This sometimes results in “N+1” database query operations – a problem to be solved later in development, once the scaling and performance attributes are understood.  GraphQL provides a means to do those optimizations, which we defer until they are needed.

Database

At the database layer, we innovate the least. We typically recommend a common and well-proven relational database. Our default is PostgreSQL, although sometimes customer deployment needs may result in MS SQL Server or another RDBMS.

Product development launch efforts are about speed, so we don’t the database schemas by hand. We define data structures in program code then use the tooling (for example, a JPA implementation) generate the schema. Data migration is also generally not an issue in a short-term launch effort; those come into play in a longer lived project which goes in production with data to preserve across versions.

Deployment

Large, long-term software projects will end up with specialized operations experts who shepherd them through critical infrastructure – but this post is about short product launch projects. These projects need to be made visible for review, demonstration and so on, long before the organizational wheels can turn for serious deployment infrastructure.

Therefore, we typically simply deploy the software for demonstration, to a cloud server instance of some kind (AWS, Google Cloud, Digital Ocean, etc), with minor scripting or tooling to automate deploying new versions frequently (sometimes even at every commit) during development. This is not scalable, and not nearly as automatable as more robust solutions, but it is a perfect starting point for something to put in place right away.

Quality

During a short-duration project effort, we write code quickly, but still keep a close eye on quality. Writing good code typically results in faster progress, beyond the timescale of the day or two.

What about the rest of the tools and practices?

Reading back over this description of the choices we make to launch an effort quickly, you might get the impression that we don’t deploy modern techniques, that we haven’t heard of all of the latest (or decades worth) of buzzwords. On the contrary, we have a full array of additional techniques to apply as a project grows in scope, size, and duration.

Micro Services

For certain projects, a micro service architecture produces numerous benefits. But even the esteemed software architect Martin Fowler suggest starting with a monolith: https://martinfowler.com/bliki/MonolithFirst.html

Unit testing

During a product launch, much of the code is boring ordinary use external libraries, which needs very little unit testing. But a project that grows beyond the initial launch will need lots of unit testing around any logic of complexity or interest.

API testing

As APIs become more complex, they warrant thorough test coverage – so a project that grows will get that coverage.

E2E Testing

A product launch effort typically yields a modest number of screens, undergoing rapid change, therefore unsuitable for browser-based end to end automated testing. Therefore, we skipped that during the launch effort.

We don’t forget though – here at Oasis Digital we are very big fans of automated E2E testing, and have seen it pay off on a daily basis, for most any project that lives more than a few months.

NoSQL

NoSQL can solve a great number of problems, and we recommend this type of data store when it is needed. This rarely occurs in the first month or two of the project when the vision and user experience are still being understood.

CQRS / DDD / ES

We have used these techniques extensively, as you can read about elsewhere on our blog. But we mostly set these skills aside during a fast product launch, these are things that pay off its scale but which can make it hard to get to scale as they are allowed to consume too much time early in a project.

Planning and Methodology

During a short launch effort, planning happens primarily via a whiteboard, or spreadsheet, or similar tool. If the values proven in vision works, a project may grow large enough to warrant more complex planning and project management.

Issue tracking

Although we work extensively with issue tracking technology (our sister company builds add-on products and provide services around Atlassian Jira), during a product launch effort our issue tracking approach is intentionally very lightweight. Issues that won’t get attention during the launch effort, are simply listed somewhere tersely. Issues that need attention right away, typically get attention right away, or at are tracked in some lightweight manner. A product launch effort that lasts only a few weeks to a few months might or might not use a “real” issue tracker in that short time, while an effort that grows to a long-term project will use one extensively for tracking, planning, support, etc.

Much more

This is just a short sampling of practices and how they may apply differently at the beginning of a short effort versus late into a large one.

Frameworks and commercial ecosystems

Or, “why we don’t teach Aurelia”

Here at Oasis Digital and its sister company (Expium), we offer training and services concentrated around various languages and frameworks:

  • Angular
  • TypeScript
  • Node
  • The web platform in general
  • JIRA, Confluence, and other Atlassian products (Expium is an Atlassian Solutions Partner)

There are many reason – technical, history, intentional, and accidental – around how we ended up with this set of technologies as our 2017 training and consulting focus.

I was reminded of one key factor today while watching a video from last year of a talk by Rob Eisenberg. Rob is exceptionally sharp, and seems to have a good sense of taste in designing frameworks for developer satisfaction. But I found myself in disagreement over his thoughts around web framework adoption. Rob argues that frameworks like his (Aurelia) are stronger, better choices to build on than frameworks like Angular and React, because first-party training and support services are available for Aurelia from the makers of Aurelia. This initially seems like a compelling pitch, I can see how it would woo some decision-makers. Here is a snippet of one of the slides along these lines, pointing out first party training as an advantage:

But I think ultimately this works out much less well than Rob describes. Why? Because this first party set of training and consulting offerings leave less space for a thriving commercial ecosystem to develop around a framework.

Let’s look at Angular for example. Here at Oasis Digital, we aim to be a leader among many firms around the world, who provide training, consulting, etc. for Angular. Our customers are quite happy with the availability of these services from many different companies; it reduces their risk and means they can shop around for the best fit. Moreover, because Angular has created opportunities for companies like Oasis Digital, it has facilitated a growing commercial ecosystem revolving around the framework. Much the same applies, for example, to React and Vue.js. This is a virtuous cycle. The non-service-offering core team leaves room for others to provide services, which in turn makes it easier and safer for customers to adopt the framework.

(A second example at Oasis Digital’s sister company Expium: Expium focuses entirely on the Atlassian product suite. While Atlassian offers online video training options, Expium’s offerings include things like live human training that don’t compete directly with Atlassian’s offerings. Atlassian enjoys a thriving commercial ecosystem.)

Of course it would be possible for companies like us to offer training and consulting focused on Aurelia. But we don’t want to do that; we like the people responsible for the framework. If we offered services for Aurelia, we would have an inherently competitive relationship with the company behind Aurelia, vying for the same customer opportunities.

This situation applies to various other frameworks and other technical specialties that we could choose to focus on; with so many choices, it inevitably feels wiser to choose those where we can be allied with the core teams rather than in competition with them.

I believe that overall, this is quite important in understanding why some frameworks gain enormous momentum and others do not. Creating this kind opportunity for a commercial ecosystem is an immense competitive advantage to those companies who can offer a framework without needing to build a business directly around it – like Google and Angular.

 

Analysis Projects – expedited, insightful project review

We deliver a wide range of services at Oasis Digital – training, mentoring, development, and plenty in between. Occasionally we do an “analysis project”, in which we thoroughly review a project’s code and related assets, then prepare a written report and discussion of recommendations. Customers typically request such an analysis for the usual business reasons:

  • Acquisition or investment diligence
  • Budget has gone awry
  • New opportunity drives a decision of whether to update or replace a software system
  • As part of a technology “bake-off”
  • Etc.

Although we work across a wide range of technologies, our project analysis work is focused on the technologies that we use most often, such as Angular, React, TypeScript, and Node, and many related technologies. The projects we analyze often have portions which span other technologies, and if they stray too far from areas where we have the greatest expertise, we caution customers that our analysis of those portions will be less deep.

Typical Analysis Project Scope of Work

The scope of work on an analysis project typically looks something like this:

  1. Meet with customer project managers and product owners to understand the business purposes for a project.
  2. Meet with customer developers to understand the technical background and status of the project.
  3. Study source code, documentation and other materials provided by customer.
  4. If there are multiple candidate sets of source code, use comparison tools and try to determine which is the most complete or up-to-date.
  5. To the extent possible, set up a development/test environment to execute the project. For some projects this is straightforward, rarely it proves impossible, usually it is somewhere in between.
  6. Assess use of third-party tools (open-source and commercial).
  7. Prepare a written report of our assessment and recommendations for the project.
  8. Prepare a screen video code review of some critical portions of the source code, if appropriate.
  9. One or more meetings with customer stakeholders (developers, product owners, etc.) to discuss the results.

Deliverables are:

  1. the written report
  2. optional screen video code review
  3. discussion meetings

The scope and deliverables for an analysis project do not include features, bug fixes, or other development work.  We do many projects which deliver those things – but those are not analysis projects,.

Typical analysis project report outline

An analysis report will typically include sections like so:

  1. Introduction and summary of customer situation
  2. Summary of analysis work performed
  3. Analysis of how data is stored and managed in the software as it executes
  4. Analysis of how data is persisted long-term, i.e. how data is stored to a database/files/backend/etc
  5. Listing of third-party code libraries used
  6. Assessment of the currency and future prospects of such libraries
  7. Analysis of coding techniques used in the software, relative to popular or best practices
  8. Commentary on any potential security issues we notice (although as a company we do not specialize in security analysis)
  9. Assessment of the user interface
  10. Assessment of the overall amount of functionality relative to the quantity of source code
  11. Overall assessment of the maintainability and future prospects of the code

Of course the specific outline varies from one analysis to another, and we often have special requests from a customer to look in particular depth at one area or another.

Schedule and Cost

Depending on the size of a project, the duration can vary from days to weeks, and the cost can also vary widely. We generally can quote a price, once we know some statistics about a project, and discuss the project with someone who is familiar with the code.

For example, on a project that uses AngularJS, we would inquire about:

  • How many Javascript files?
  • How many total lines of JavaScript code? Ideally the tool used to count this would skip blank lines and comment lines. For both this line count in the file counts, it is best to count only project code – sometimes these numbers can get artificially inflated by third-party code you are merely using which happens to sit in a library directory in your project.
  • How many template files?
  • How many total lines of template?
  • How many angular directives?
  • How many angular components?
  • How many angular services?
  • How many different third-party JavaScript “widgets” are used?
  • Mention any major add-on libraries that the application uses extensively; for example Lodash.
  • Subjectively, how many distinct screens/pages are there in the sites/applications? Is there a large amount of code driving a small number of complex pages?

Sounds good, how do I buy this?

This is just a blog post, but if you contact us we can discuss your particular project in need of analysis, get a sense of its size, and prepare a service agreement to begin.

Angular universal / server side rendering

The current state of server-side rendering (so-called “universal”) for Angular is somewhat in flux in mid-2017. There had been an early Angular Universal effort by an outside group, which has now been absorbed into the core Angular team at Google. They are working toward a new release (to become part of an Angular release) which integrates it tightly is a fully supported first-class piece of the Angular tool suite.

Very eager developers, it is possible to use some of these tools now; but it should become easy and mainstream in the coming months. The primary use cases are:

1) SEO

Site/applications which have publicly exposed pages for which search engine optimization is needed, prefer to statically render (and serve) the key SEO pages. Historically this was vital, because search engines did not execute JavaScript on the pages being indexed. However, for at least the last several years, Google and its other top-tier search engine competitors do execute JavaScript on a page, so the SEO use case is not as important as it used to be. Many still prefer to statically render pages for maximum SEO, nonetheless.

2) Progressive Web Applications

This is the current leading edge of aggressively performance web applications for mobile devices. The idea is to statically load the outer “shell” of an application with some initial static content displayed, then replace that initial content with fully dynamic content a few seconds later. That initial load involves the smallest feasible amount of HTML, CSS, JavaScript.

PWA is a compelling idea, but there are practicalities to its appeal:

* PWA is mostly relevant on mobile devices. An optimized Angular application will load very quickly on a desktop machine regardless.

* PWA is most important on down-level devices and networks. It doesn’t make as much difference on those of us sitting in well-networked places on LTE with current generation smart phones – or on fast corporate networks.

* PWA matters most on the first load of a page/application. After that, many of the assets will be cached so the real content will load very shortly after the progressive pre-render.

The coming default

We believe the tooling will eventually work so nicely, that static pre-rendering and PWA become straightforward, or even become the default path for new applications. But keep in mind the practicalities above – for many use cases, pre-rendering and PWA make sense to adopt only when the tooling makes it very straightforward. Developer efforts between now and then probably pay off more if directed toward application functionality and polish.

Angular 4 rc.1 AOT build options – with example projects

From the summer 2016 production release of Angular, most users have treated AOT as a future curiosity. As of late fall 2016 though, many (particularly those working on larger applications) have been much more eager and interested to use AOT. Here at Oasis Digital, we have recently updated our Angular 2+ curriculum to ensure the numerous code examples used in class are AOT-ready.

Although most of our production Angular 2+ work uses the Google-sponsored Angular CLI (which was excellent AOT support), we’ve also been working with various alternative tooling stacks. Some of our customers integrate their Angular applications with broader build processes and are looking for more fine-grained control than they get with the official CLI.

Last week, Angular 4 rc.1 shipped with additional library packaging bundles, FESM and ES2015 FESM. These should support tighter production builds them before, and more easily; the official CLI does not take advantage of these yet (though I expect it will soon), and I was eager to experiment.

The results

https://github.com/OasisDigital/angular-aot-es2015-rollup/tree/master

In this example, the build is performed using:

  • AOT (ngc
  • Rollup
  • Buble
  • Uglify

See the README in the project for a lengthy explanation of how it works and why these tools were chosen. It was mostly straightforward to make this work; the configuration is quite simple. However, as of the beginning of March 2017 there is an important Rollup plug-in which does not yet have the ability to consume the new Angular ES2016 FESM bundles. To work around that, I published a (hopefully temporary) fork of that plug-in, “@oasisdigital/rollup-plugin-node-resolve“.

Another variation

The boring, excellent, proven, and still frequently updated Google Closure Compiler can often produce better results than newer, hip tools. Therefore the following variation/branch:

https://github.com/OasisDigital/angular-aot-es2015-rollup/tree/closure-compiler

…replaces a couple of the tools with Closure Compiler. It uses only:

  • AOT (ngc)
  • Rollup
  • Closure Compiler

With fewer tools, it produces a smaller results. The configuration is slightly more complex (mostly because the Closure Compiler JavaScript port is not quite the same as the Java edition yet), but is still quite manageable.

I have not yet compared this with an even shorter stack (using Closure Compiler for the tree shaking as well), as there are examples around already doing that; but I expect an upcoming enhancement to Closure Compiler will add the “es2015” package field support needed for the ES2015 FESM bundles, once that is in place I am curious as to whether Rollup or Closure (both very respected as excellent tree shaking tools) will produce tighter results.

Why this matters

For projects deployed on an intranet, it’s possible that none of this matters. A very large internal enterprise project might ship a total of 6 MB of compressed JavaScript (hopefully divided across various bundles littered on demand) with the default tooling, or 5 MB with tweaked tooling. That won’t matter across a gigabit network with people mostly using an application frequently (and therefore with the JavaScript mostly in cache.

But not all projects are huge or internal. Angular is also well-suited for medium-to-large projects deployed on the open Internet to a huge number of sporadic users. For these users, who might be on slow connections, saving bytes counts. Faster load times translate to more user engagement. Better production bundling expands the reach of Angular two more kinds of projects.

The above is not even counting mobile; as Angular mobile application development continues to increase, the tightest possible production bundles will matter more and more.

 

The Heart of BI / OLAP is your data

There are plenty of vendors eager with a sales pitch for BI/OLAP projects, eager also to give you the impression that all you need to do is buy their product. This is wrong, perhaps dangerously so, because **the heart of BI / OLAP is your data** and the core challenge is to transform your data into a form where it can be easily **and correctly** analyzed.

The principles of operation are similar regardless of which products you choose. Your toolset will consist of, at minimum:

  • An OLAP tool, with or without a RDBMS behind it
  • An ETL tool, which might be a software product or might be a set of scripts
  • Hardware to run it on, chosen and configured to server an analytic load well;
    this could be hardware you own, or a SaaS or cloud offering
  • Configuration thereof

But this analysis understates perhaps the most important part. ETL needs extensive configuration (for moderate cases and powerful ETL software, and some luck) and more likely, carefully crafted software to transform business data from whatever form it lies in, to a suitable shape for analysis.

Of course I cannot help but mention that Oasis Digital works on such projects; but regardless of us, effective OLAP involves an astonishing amount of “getting your hands dirty” digging in and understanding the precise meaning of bits of data flowing out of one or more (perhaps many more) operational systems. This work is arguably so unpleasant that it leads many organizations to skip OLAP, but that actually misses the point. The work can’t be avoided, without also avoiding the full value that could otherwise be obtained from a correctly populated analytical data store.

Alternatively, the work could be skipped, if you don’t mind incorrect analytical data and incorrect conclusions drawn from it. This doesn’t seem like a strategy for success.

 

How we use Git, and why

Here at Oasis Digital, we use Git for source control for nearly all of our projects. There are numerous different ways to use Git, and after many projects we have evolved on a set of effective practices. We have found the approach described here works very well for almost everything we do, though of course as a consulting organization we sometimes adjust things to meet a specific customer need.

Why Git?

Ubiquity

A decade ago, distributed source control arrived suddenly in the mainstream, after years as a niche segment. There were a number of contenders, but Git “won” by a large margin. Today Git is essentially the default choice, the powerful choice, the ubiquitous choice.

Incidentally, Git’s endless flexibility is a major reason it won the race. Various other competitors typically had better ergonomics, easier adoption, easier understanding… but less flexibility. Flexibility means that organizations large and small can use it in a way that meets specific needs.

Technical excellence

Unlike some other systems, we have never lost a line of code due to a defect in Git. Further, we have never needed a permutation which is fundamentally impossible with Git. We have found that, within the confines of our project sizes, it scales extremely well. (Though see a section at the end about limitations in this area.)

Multiplatform

Our projects are often developed across Linux, Windows, and Mac. Git works very well across all of them. Notably, it offers solutions (rather than a “head in the sand”) for differences around line endings among platforms. Yes, it is inconvenient to deal with line ending differences. Git has the tools to do it and get good results, without trying to pretend that one platform is another.

Distributed

Git can operate without a network connection, and more importantly, it operates locally at the speed of a local machine rather than at the speed of a potentially overwhelmed, faraway server. 90% of Git operations are completely local. This is an enormous benefit during daily use, though it has a downside – more risk of a developer forgetting to push code to a server. We handle that concern so well in other ways (project team discussions) that it has never caused difficulty.

Ecosystem

Due to ubiquity, Git has spawned a vast ecosystem of related tools. Nearly every editor and IDE understands Git. Nearly every build or continuous integration mechanism understands it. Nearly every code review tool understands it. There are numerous graphical Git tools for every platform, it is not dependent on any one vendor or team to continue producing quality results.

How we use Git

Use it as an expert

As with most other tools we use, we are committed to expert level mastery, not muddling through. Oasis Digital developers learn to understand the fundamental Git data model of a tree of commits each containing a tree of files. We learn the essential operations and common variations. We learn to understand what the commit tree looks like, what we wanted to look like, and to choose the right command to get from one state to the other. We are “source control nerds”.

We believe this makes sense because source control is not merely an ancillary tool; this is because change management is deeply fundamental to robust long-lived systems. Source control is not an inconvenience, it is an accelerator.

We to treat (portions of, read on) the Git commit tree as putty in our hands.  This is an intentional trade-off, versus the notion of using a restricted subset of Git, but we believe for our use cases is the right trade-off.

Develop on branches

We develop on branches, not on master. In all but the smallest projects, branches go through review and discussion before landing on master.

We use many branches, large and small. We don’t pointlessly mix unrelated changes in the same branch. Git switches among and manages branches almost instantly, so the cost of using additional branches is nearly zero.  We have found that the mental overhead of managing more than one branch using a tool, is much less than the mental overhead of juggling multiple unrelated changes in the same branch, a phenomenon we see regularly when developers use tools which make branching difficult or slow.

Branching model – varies

We manage those branches differently, depending on the needs of a project:

  • small branches, directly off master
  • shared branches, as needed
  • release branches, to support old releases
  • development branches
  • trunk-based development, with small or large branches

 

Master always works

Master (and for more complex projects, certain other branches) always works. Code is reviewed and tested before going on master, not only after. If you read and adopt only one bit from this whole page, this is the most important. Review and commit and make the code good before it goes on master, don’t put junk on master and hope for a drive-by review-fix later.

Master strictly improves

Because we test and review and fix code before it goes on master, master (again, for complex projects, certain other branches) strictly improves. Each master commit adds at least one improvement or fixes at least one problem, without making the software worse. Of course we do not reach the standard perfectly every time, but this is the standard we aim for, and we reach it most of the time.

Immutable master; mutable development branches

Except in rare cases, once commits are on master we treat them as permanently immutable. They form a long-term record of the history of the project. They are a curated, comprehensible telling of the story of the development.

The work performed on branches, is iterated repeatedly on branches. Sometimes it is squashed and often it is rebased. Only when it is ready (to act as a strict improvement to master) does he get a final review, squash, rebase, merge. (As with everything else, except for certain special cases.)

Incidentally, this means that we very rarely have a merge commit on master. The long-term history of our development efforts are easy to read, as a mostly straight-line of medium-sized (not too large, not too numerous) commits.

Most of our developers on most of our projects are not even able to push to master – and are hardly affected by this restriction and daily work. Even with this process, a typical developer will have a commit land on master between one and a few times per week – Depending on numerous factors around how fine-grained the work is divided in the project.

Commit early, commit often, commit anything, push

The corollary of our high bar for work that goes on master, is an extremely low bar for work that can go on to a branch and be pushed. We rarely go to sleep with uncommitted on pushed code. When we need another developer to look at our work in progress, we commit it and push it as work in progress.  When a new developer starts, they often commit and push code to a branch the first day. By keeping this bar low, we provide the maximum opportunity for new developers to become fluent with source control and to use our tooling as a communication mechanism (“here is some code, when you look at it for me?”) Early and often.

Squash out the mess

There is an inevitable, human tendency to be careful with anything that will be “part of your permanent record”. This is built into all of us, though it seems to be more pronounced in some cultures than others. But by combining our very low bar for what can be committed and pushed, with a policy of squashing minor steps together to yield an aggregated, high-quality change, we moderate this tendency very effectively.  Code can be pushed any time for just a casual look. It won’t be part of the permanent history until it is good enough. No fear.

Left to its own, the tendency to be careful would force developers to “raise the bar” of what gets committed or pushed. A rational developer (regardless of policy) in such an environment must be careful about what they put in source control. We have found that this inevitably pushes developers to do more of their work without source control. For example, by shuffling files locally outside of the source control tool, by leaving work on pushed, etc.

Source control is a very powerful tool, and a low-bar-push-often-squash-rebase approach enables that power into the hands of every developer, individually as well as all developers working together.

But squash carefully

Still, squash and rebase are potentially troublesome features. So when we squash, we do not squash arbitrarily. We aim to squash groups of related changes, worked on along a path to achieve a specific feature, but not to squash unrelated work. One minor caveat though: to tell the story of the development in the most comprehendible way, the permanent master commits should be both of medium-size and medium number. Therefore we sometimes squash together a set of individually very small changes, when we can do so without creating difficulties.

Use a GUI

Many very skilled developers are fond of command line interfaces, for many good reasons. The Git command line interface, while slightly problematic in some ways, is extremely powerful for making changes. However, it is not particularly well-suited for understanding a Git commit tree, especially on a busy project with numerous concurrent branches. For this reason, when working heavily with Git we always use a graphical interface at least to visualize and understand the commit tree, even when we use the command line for manipulation. There are various high quality choices for GUIs on each platform, and on all platforms the built-in “gitk” interface is very helpful in a pinch to understand the commits – without any additional tool selection, download, or install.

Work together, learn, teach

We work together regularly. A group of developers working on the same project will set in a collaboration space together (inviting remote members online) at a giant screen attached to a fast development machine. We work on the hard problems together, we refer you tricky code together. We learn and teach together, how to use all of our tools, including Git.

Caveats, limitations, and context

Small, medium, and large, but not huge

Git, and the practices here are useful on our projects of small, medium, or largish size. Beyond a certain size, many of the practices would still work but Git becomes awkward. For example, some of the largest software operations in the industry (Google, Facebook) use a “monorepo” with all of the code for all of their projects and nearly all dependencies thereof. A different kind of toolset is needed for such extremely large repositories.

Source code, but few binary assets

Further, we keep small, medium and somewhat large collections of source code in Git; when our work has occasionally involved many large media assets, we have used other means to track them. Git specifically, is probably not a suitable choice for (for example) a large-scale game development effort with hundreds of gigabytes of binary assets.

Skill and understanding needed

Here at Oasis Digital, we are not in the “get a bunch of people to grunt out some code” business. Instead we are in the “recruit and hire good people, and help them become great” business. This is necessary for us, because of the premier customers we serve, and because we not only develop, we also teach. We are all about deep mastery of tools. Our Git approach fits very well with this context.

We’ve sometimes heard the objection to Git, that it is difficult for less skilled developers to understand fully and use correctly. This is possibly true, but it is not that important to our context – and we think it is not that important at all. Anyone with the capability of understanding software enough to develop quickly, efficiently, and with quality, certainly has the capability to master Git.

With great power…

Even with good understanding, Git is still relatively easy to misuse. It offers numerous very powerful commands, it is like a chainsaw and a machine shop. It is not like a set of safety scissors.

We have found that this power punishes developers who don’t look at a graphical representation of the commit tree regularly. It rewards developers who do.

Syntax

Even keeping in mind the power of the Git command vocabulary, it’s CLI syntax leaves much to be desired. It shows clear signs of having evolved awkwardly, and had early accidental complexity retained for backward compatibility.

However, we have found that developers who have the discipline to look at a graphical representation of the commit tree, generally gain a better understanding of the operation they want to perform and are more likely to perform correctly even when using the CLI.

Beware neutered GUIs

Some Git GUI products aim to simplify Git “for the masses”. In the process they managed to do a terrible job of the most essential function, visualizing and understanding the commit tree. Beware of a tool which resists showing you this tree. Even following our practices described here (where master will be a straight line most of the time), it is necessary frequently to understand a bunch of concurrent possibly tangled branches when a project is under heavy many-developer work. The right GUI makes this fast and easy.

Churn

I think the Git community is now on the third major mechanism for subproject use. We avoid subprojects as much as possible, but experience some frustration when we must use it.

Revisionist history

Our approach clearly and intentionally yields a revisionist, streamlines history. As described above, the resulting history is crafted for understanding, and for (rarely) backing out the final set of related changes for some work rather than miscellaneous partial changes. Still, some people are uncomfortable with the revisionism, and prefer a full record of every step along the way. This is a trade-off, where we have found the revisionist approach has more upside than downside.

Discipline needed

Even with the restrictions, it still requires discipline, to avoid creating a branch from a spot in history (something not on master) that will change in the future.

The Oasis Digital Spectrum of Services

In the early years of Oasis Digital, we offered exactly one service: outsourced software development contracting. Since then, we’ve expanded to a spectrum of related services. The result doesn’t fit in an “elevator pitch”, but it meets the needs of customers much better. Our “spectrum of services” will be more clear on our main website and elsewhere over time, but this blog post explains it as succinctly as we can. Ranked in approximate order from smallest to largest, we offer:

1) Free technical content

Our expert developers/trainers speak and write about relevant technical topics, and the results are almost always freely available: talks on our YouTube channel, posts on our blog, our twitter accounts, and so on. Further, we attend various conferences and are always happy to speak to people who come talk with us there.

2) Tickets to public classes

We teach on several technical topics, most prominently Angular Boot Camp. Class tickets are a great fit for an individual developer or small group, who can purchase, then attend online or in person around the US and occasionally around the world.

3) Private training classes

To train a whole team at once, we offer private classes, both online and in-person. Private classes can also easily be extended with add-on days of customized consulting and training, for customers looking for added value. Some customers engage us for a series of private classes.

4) Application assessment

An application assessment is a short consulting engagement (typically 1 week, with 1 or 2 of our experts) in which we meet with a new customer in-depth, to assess an application (or understand the vision of an application). The assessment includes a written report, and (if needed) a proposal for future recommended work.

5) Ongoing expert assistance

Oasis Digital can provide ongoing expert assistance, in a retainer-like arrangement. We regularly meet with your developers, to help with questions, issues, code review, design guidance, and implementation of key areas. We have different packages depending on how much assistance each month you need.

6) Agile product development

In an Agile development project, a Oasis Digital works with a customer on an iterative basis, prioritizing features (“stories”), responding to changes and guidance. Such a project is especially amenable when the product vision is established but feature needs are still evolving. An Agile project is straightforward to contract and price (based on the team size), and can start quickly then last as long as needed. We often begin Agile projects with Oasis Digital developers, then gradually integrate customer developers over time for an eventual handover. The project style is also well suited when Oasis Digital is joining an existing effort already in progress.

7) Scoped product development

In a scope product/project, the features needed are worked out in advance (a scoping effort might be part of an application assessment, for example) so that Oasis Digital can provide a price and schedule to achieve that known list of features and surrounding goals. This style of project is decidedly less agile (changes and additional features are generally implemented after successful delivery of the initial scope), but can also ultimately be more efficient – our experts are especially adept at skipping directly to a high quality approach, avoiding false starts and reducing rework during iteration.