Posted by: Daniel | September 10, 2008

What is a “Software Architect?”

When people ask me what I do for a living, I usually say I’m a computer guy and leave it there. But if they want more detail, I tell them I’m a software architect. When I give that answer, I often wonder if I sound pretentious, like those who say “sanitation engineer” instead of “garbage man.” Do those who ask the question think a “software architect” is just a fancy way of saying “computer programmer?”

Well, although I am a programmer of sorts, what I do is different enough from standard computer programming to require a different name.

An architect is responsible for the strategic health of a technology portfolio. “Health” embraces many different considerations, such as how maintainable the code is, how easily it can adapt to changing business requirements, how technically differentiated it is from the can-do assets of competitors, whether the portfolio makes appropriate use of (and embodies/generates) trade secrets and patents, how performant and scalable it is and whether those characteristics are a good match for the “sweet spot” customers, obsolescence, and so forth.

The overall goal of the architecture function is to maximize dev ROI and technical competitive advantage over the long run.

By its very nature, architecture is broad in scope and overlaps with many other disciplines. Because it concerns itself with fragility, boundary conditions, and theoretical limits, it needs to interact with design and actual code, and architects must be proficient engineers. Because it cares about supporting potential uses beyond the confines of a specific release, architecture must consider the market landscape from both a business and a technological perspective, and it must understand when short-term compromises carry long-term risks.

The duties of a software architect include:

1. Meet with Product Management and executives to understand their business/market vision. Use this understanding to inform a perspective on the relative value of various technical tradeoffs.

2. Be a keeper of a technical vision/roadmap that supports the business/market vision optimally. Give feedback, based on that vision, about the technical tradeoffs implied by various potential business plans before those plans are codified into a plan of record, or before they are accepted as a change request.

3. Articulate to engineering the technical guidelines that should inform a new release, including the considerations behind various tradeoffs — and get formal buyoff from both engineering and PM on those guidelines. Participate in design reviews to guarantee that appropriate tradeoffs are honored and embodied correctly.

4. Do forward-looking research and proof-of-concept work to drive risk out of potential directions on the technical roadmap.

5. Identify/sponsor/promote/create high-value innovation. (“High-value” is evaluated against vision…)

6. HR-related stuff: Provide leadership and mentoring to other engineers. Evangelize the technology portfolio to other stakeholders (internal and external). Vett IP for patents and M&A. Be a voice. Etc.

Of these duties, items 1, 2, and 3 sometimes get lost or watered down if an architect is just a senior engineer. In that sort of environment, PM may convey a vision to dev without involving architects. As a result, the team has conversations about feasibility or cost of implementing, but rarely if ever about whether a requirement is a good idea in the first place. Whether a requirement is a good idea is more than just a question of whether customers want it, whether it can generate revenue, and whether it can be built — it also must take into account things like technical opportunity cost, long-term baggage, effect on the expertise and focus of the engineers who will build it, technical differentiation, and so forth.

It also might be worth highlighting some things that I *don’t* consider part of an architect’s job, for the sake of clarity:

* An architect is not necessarily a lead engineer. Lead engineers translate architectural guidelines and vision into implementation, not just in the design phase, but all the way through RTM. Lead engineers work for dev managers and are accountable directly to them. Lead engineers might have a technical director pay grade, but whether they are asked to function as architects is a separate question. When lead engineers push back, it’s usually about whether an implementation works or is reasonable, not about whether a vision is the right one. Lead engineers may “own” a particular facet of the technology portfolio through multiple release cycles, and this cross-release perspective is architect-like. However, lead engineers are far less likely to radically question the value of what they own than an architect, because an architect is looking at a bigger picture over a longer horizon. When Bill Gates told every MS employee to drop what they were doing and spend a month thinking about the Internet because the company wasn’t “getting it,” he was being an architect. The lead engineers on all the projects and components that ultimately got ditched so MS could go chase the web wave would never have done that.

* An architect is not a dev manager or dev director. Dev management coordinates the work of a team that delivers product to spec. Dev management scopes work and speaks with authority on whether something is doable with what schedule and what resources. Dev management is extremely focused on release cycles. If we were constructing buildings, dev managers would be foremen — reading blueprints, hiring and monitoring subcontractors, cutting checks, giving status reports. A dev director or Sr. Dev Director would be the general contractor. But neither the general contractor nor the foremen would decide to put an elevator shaft in a different spot without talking to the architect who calculated the load-bearing capacity of the walls. Like lead engineers, dev management should understand technical guidelines provided by an architect, and should be accountable to work within them, pushing back to the architect as necessary. This is parallel and analogous to the push-back that dev management provides to PM when the schedule is threatened.

* An architect is not a product manager. A product manager proxies customers and builds business plans for releases of products. A product manager says with authority, “This is what the market wants, and we can make X selling it.” These business plans should represent our best understanding of revenue-maximizing choices. But without an architect to provide feedback, these plans typically have a one-release horizon — whatever maximizes revenue in the next release is assumed to be best. Or else we have a long-term business perspective, but implementers receive little guidance about technical tradeoffs that are aligned with an appropriate evolutionary path; the technology becomes a patchwork quilt of kludges and dead-ends that is progressively more expensive to enhance and less useful as a competitive weapon.

Posted by: Daniel | September 5, 2008

Annotating the Web

Bookmarks just remember a location. That’s kind of nifty, but even with all the power of del.icio.us or a similar service, it’s not something that keeps me awake at night with enthusiasm.

Why don’t we move beyond simple bookmarks? Let’s let people remember what they were thinking when they read a particular piece of content. Let’s figure out a way to annotate web pages so that if you come back to that same page, your annotations show up again.

Annotations could take the form of text (with all the hypertext and multimedia features of html itself), ink (to scribble or draw on top of a page, circle key elements, etc.), audio… Annotations could be a layer on top of a web page — something you could toggle on or off. You could even share your annotations with someone else so they could turn on your layer or compare yours to theirs.

Imagine this in an educational setting. An instructor wants you to read an essay about existentialism in Waiting for Godot. The instructor takes a few minutes to highlight the parts of the essay that she finds particularly interesting — underlining a couple sentences, jotting a note about a section that’s out of sync with her own research, etc. When you read the essay, you can turn on the instructor’s criticism of the essay and add your own. Maybe other classmates can share their ideas with you as well.

To make this maximally powerful, you’d want to be able to anchor your comments to particular slices of content on the page. Over time, if the content of the page or the rendering of the page changed, you’d want your annotations to adapt. Example: you disagree with an author who claims that “peptide bonds are a boring subject that any first-year biology student can afford to sleep through.” You highlight that sentence on the page, bring up a context menu with a right-click or control-click, and on the menu one of the options is “annotate.” You create your annotation much the same way you’d create a bookmark. (You could also anchor annotations to graphics or other elements on the page pretty easily.)

You visit the same page 3 weeks later. The .css behind the page is different now, and the content has been extended (e.g., it’s a blog and a dozen people have commented). And you’re using a different browser, on a different OS, possibly in high contrast / large font mode where you were in normal mode before. The point is, visually the page looks completely different. But the annotation engine searches for the anchor text for your annotation, finds it, and basically offers a tooltip to pop your annotation at the correct location on the page.

(By the way, the annotation mechanism I’m describing here might be a more interesting way to accumulate blog comments than the current way which is email thread-like. Instead of reproducing the salient portion of an earlier thread contributor’s text in your own, and then adding your words after, just anchor your annotation to the relevant portion of existing content…)

Lastly, imagine what we could do if we allowed a user to enumerate all their annotations. Annotations could be organized by key word of the page they apply to (and, to make them even more findable, by key word in their anchor text). A simple query to the annotation back end server would give you a sort of personal knowlege base on any subject.

Update: I went out and did a little research and found that some of the features I’m dreaming of already exist. If you’re interested, check out this article on wikipedia.

Posted by: Daniel | August 19, 2008

Decoupling Interfaces as Versions Evolve, Part 3

This is part 3 of a series. You can read part 1 and part 2 as well.

Quick Review

We want all the encapsulation and data hiding benefits that interfaces provide. We want to be able to version our interfaces so consumers can depend on them reliably, but we don’t want the producer and consumer of an interface to have to coordinate tightly. We don’t want the producer of an interface to have to version so often that there’s a built-in disincentive to follow best practice. And we want all the compiler and IDE benefits that early binding typically offers to a programmer.

I claim that no current solution really provides all of this — not COM, not SOAP-based web services, not late-bound REST web services.

Fear not.

Summary of Solution

  1. The provider of an interface and the consumer of an interface each conform to a compiler-enforceable contract (.wsdl/.idl/etc.), but unlike the traditional approach, these contracts are allowed to differ.
  2. The test of whether the two interfaces are compatible is not done by traditional casting, but by testing the contents of the two sides for semantic equivalence – a consumer has a compatible interface if it is a strict subset of the provider’s.
  3. The consumer is required to write wrapper classes that forward from its own interface to that of the provider. (Using a language that supports reflection, like Java or C#, makes this task trivial).

Alternative Approach

Alternative Approach

The Gory Details

This solution could be built on top of COM, RPC-over-soap-style web services, or a RESTful service interface more analogous to document-oriented web services. Other environments such as CORBA/EJB may also be candidates, though I am less familiar with the details there.

Most SOAP comm pipelines get a remote object and deserialize it to a tightly bound object type in a single step, using a type cast as a runtime check that the remote source meets the calling code’s expectations. Such code would have to change so a remote object is fetched and deserialized in an initial step, and subsequently, the standard cast is replaced with a function that creates a wrapper object from the local interface if compatibility tests pass.

TryCast Pseudocode

TryCast Pseudocode

In COM code, the analogous initial step must return an IUnknown; the second step consists of composing the semantic union of all interfaces the IUnknown supports, and then using that überinterface as the basis for compatibility testing. Since IUnknown does not support enumeration, the semantic union of all interfaces in an IUnknown would require a list of possible IIDs to perform a series of QueryInterface calls, or a low-level analysis of the object’s vtable.

In a RESTful document-oriented web service, a URL returns an xml document that describes an arbitrary object using structural elements that do not vary across returned object type. For example, instead of

<book><title>Dragon’s Egg</title><author><fname>Stephen</fname><lname>King</lname></book>

you have

<doc><prop name=”title” type=”string”>Dragon’s Egg</prop><prop name=”author”>Stephen King</prop></doc>

or something similar. This conveys the object’s semantic constraints along with its data, much like sending a table definition along with a tuple in response to a DB query. The initial step of deserialization constructs a generic object; the second step tests compatibility against the semantic constraints embedded directly in the document and constructs an instance of a wrapper class on success.

It’s important to distinguish between read-only and read-write usage patterns in this mechanism. Consumers of an interface that only intend to display data are infinitely backward compatible if the runtime check for semantic compatibility passes, regardless of the version numbers/guids in play under a given scenario, because the wrapper classes depend on an interface mapping that’s generated dynamically at runtime. However, if a consumer of an object wants to update its state at the source, the wrapper class must contain every property that the provider will require – or else the provider must set such properties either before serving the object or when the update is requested. Using wrapper classes rather than the traditional generated SOAP stubs is an important element of this mechanism because this allows mods to objects that a client does not fully understand.

New Approach - Pros and Cons

New Approach - Pros and Cons

Posted by: Daniel | August 19, 2008

Decoupling Interfaces as Versions Evolve, Part 2

This is part 2 of a series. You can read part 1 and part 3 as well.

Alternative Approaches to Interface Versioning

Lublinsky wrote a great article about interface versioning a while back (see page 38 of this issue of Microsoft’s Architecture Journal). This describes the state-of-the-art thinking about interface versioning in the web services world. Essentially he recommends versioning each method in an interface separately. (Sounds a lot like Win32’s approach of adding …Ex to every function when the original behavior no longer sufficed…) This approach is based on the insight that many parts of an interface will be stable for long periods of time, and that the most common kind of change to an interface is an addition. By increasing the granularity of the versioning, incompatibilities are less likely to arise for spurious reasons. This solves the classic problem where a .wsdl describes a dozen classes, a client uses only the first three, and yet the client breaks when something in the fourth class changes. However, it proliferates .wsdls and points of presence.

Another important discussion of this issue is “A SOA Versioning Covenant”, by Rocky Lhotka. This is an excellent review of the problem. (Note that the Lublinsky article, which is newer, discusses the covenant idea briefly.) Essentially Lhotka recommends that all objects accept messages (parameter lists to functions, recast as documents or self-contained packages of information); since each logical function will always have the signature DoSomething(message), the need to version interfaces goes away as long as changes just involve new message types. Instead, the messages are versioned using schema capabilities. Lhotka further recommends changing from contract-oriented thinking (X is required) to a covenant (If you do X, I will do Y). This approach has some of the same benefits as the invention, but it still relies on versioning a full interface rather than the subset someone wishes to use, and the difficulty of managing versions of messages is ignored.

Although both of these treatments (and the sources they cite in their own reviews of the problem) are nifty, they leave me unsatisfied. The bottom line is that I want to evolve interfaces whenever it makes sense, without worrying about breaking people — and I also want people who use my interface to be able to do so with confidence.

Tune in to part 3 of this series for my proposed solution.

Posted by: Daniel | July 30, 2008

Why Weakened Dev Teams Suffer From NIH Syndrome

(NIH = Not Invented Here)

So here’s the situation. Product Management says feature X must be built. Development scopes the feature and discovers that the “right” solution involves using code developed elsewhere. But unfortunately, the other team’s code uses different assumptions and will take some extra time to shoehorn into the new context. In other words, the right solution is too costly.

If a development team doesn’t have an architect who can advocate for the right solution based on first principles, then it uses the tried-and-true scope/schedule/resources lever (about its only negotiating tool) to push back.

The problem is that in this scenario, product management isn’t really forced to come to terms with the long term strategic consequences of doing things wrong. So the scope is adjusted, or the schedule is adjusted, or the resources are adjusted — but only enough to rescue the feature, not enough to do it right.

As a result, dev has to invent a half-baked alternative to the other team’s solution. Ad nauseum.

We look at the dev team and say that they have “Not Invented Here” Syndrome, because they never seem to use the solutions that other teams have built. In many cases, the real problem is that the Product Management and Dev don’t have an architect mediating and looking at the situation from the standpoint of maximizing the ROI of strategic technology investments.

Posted by: Daniel | July 29, 2008

Decoupling Interfaces As Versions Evolve, Part 1

This is part 1 of a series. You can read part 2 and part 3 as well.

The Goal

Software interfaces were invented to promote encapsulation and loose coupling. In theory this enables developing and deploying without undue interdependence, which is a very good thing.

“Why the ‘in theory’ caveat?”, I hear you saying. “Surely interfaces deliver on their promise…”

Well, yes and no. Interfaces certainly provide a nifty mechanism for information hiding if your scope of concern is a tidy programming problem over the horizon of one implementation. That’s just the sort of scenario that CS academics love to use to teach their acolytes.

But most commercial software development is done in a messier world. Versioning interfaces can cause enough headaches to water down their benefits considerably, and mainstream software development tools have not done enough to address the issue.

Immutability and Versioning

Current thinking on interface versioning calls for an interface to be immutable; each change to its semantics (as manifest in an .idl, a .h, or a .wsdl, for example) should cause a change to the interface number/name/guid. Consumers of an interface bind to a specific interface version to allow compile-time validation of interface usage. Modern IDEs typically leverage early binding to provide extra goodies like autocomplete, UML class diagrams, and doc comment generation.

This immutability is less than perfect. In non-ivory-tower development, it is common to alter the semantics of an interface dozens or hundreds of times during a given dev cycle as a team converges on the final implementation. Bob adds the DoNothing() and DoSomething() functions to IWidget on day 1, then realizes a week later that he also needs DoSomethingElse() for a corner case he hadn’t fully explored. On week 23, he decides to collapse the DoSomething functions both to DoSomethingEx() because by then the differences between them feel like they should be generalized.

If all code were written by Bob as part of a single cohesive deliverable, this evolution would be uninteresting. But suppose that on week 15, Sally gets a snapshot of Bob’s .idl, and begins to build a new component to interact with IWidget. It is critical that Sally’s expectations about IWidget semantics line up with Bob’s.

What makes this ugly is that in today’s highly distributed, highly oursourced, complex projects, Bob may not actually know that Sally is using his .idl. He may think it’s okay to keep cheating on interface immutability. Either Bob has to be obsessive about versioning his interface with each change — ending up with IWidget497 by the end of the project — or else Sally is forced to communicate with Bob that she is using his interface and needs it to be stable. Neither alternative is very attractive.

Evolution Isn’t Always Forward

Best practice is usually to require that IWidget5 be a strict superset of IWidget4. Despite enthusiastic lip service, practical considerations force us to cheat here as well. A security vulnerability forces us to start encrypting the string we return from a function. A change to the underlying OS forces us to throw an exception on a function that used to be exceptionless. Over time the assumptions about semantics attached to an interface accumulate enough drift that it is impractical to ever treat an IWidget9 as an instance of IWidget2. How does Sally know when that threshold has been passed by Bob?

And What About Deployment and Upgrade?

If you want to tease out mistakes in interface versioning, just poke at the deployment and upgrade scenarios you’re going to support. Do you require that a central manager be at least as new as all the components it’s managing? Or worse, do you require the whole system to be at the same revision level? In theory, this should be unnecessary; producers (managees) are free to expose functionality in new interfaces that older consumers (managers) don’t know about, and consumers can progressively downcast until they find a mutually supported interface, so it ought to be possible to have free variation in versions. However, in practice in rich, interdependent fabrics of services, the same actor may simultaneously provide one interface while consuming another, and the intermingled dependencies often cause ISVs to force broader upgrades than a customer would like. My favorite recent, real-world example of deployment problems is the infamous IE7 dwmapi.dll problem (see also this useful discussion of the problem).

Traditional Approach - Pros and Cons

What Can Be Done?

So if interfaces don’t provide as much separation of concerns as we wish, how do we cope?

Well, one alternative to traditional interface versioning is to do “late binding”. Only the most general characteristics of language syntax are validated when code is written; whether a particular object has a particular property of a particular data type is not tested until code actually executes. This is how interpreted languages like Python, PHP, and javascript work. It provides tremendous flexibility, and it is often the solution of choice in the free-wheeling, ad hoc universe of general web apps. I am a big fan, in many cases. I love the way RESTful interfaces support ad-hoc connections, for example.

But late binding is not a panacea. For one thing, late binding typically means that development tools can’t help you validate your usage very much. You end up writing and maintaining a lot of manual glue. For another, QA teams often push back against late-bound solutions because it increases the testing burden. Where a compiler could effectively validate millions of potential code paths at compile time for early bound code, testers struggle to achieve similar coverage. Result: bugs discovered later in the process. There is also a cost in performance and robustness that typically deters ISVs building standard enterprise or consumer applications.

There are subtler costs as well. When you late bind, you still have to use the interface you ultimately invoke, and the knowledge about how to use it has to be baked into the code ahead of time. It may not be baked in in the same way — maybe you use reflection or GetProcAddress to find the DoSomething function you’re after — but to late bind an interface, you have to early bind all the logic that handles the cases where GetProcAddress fails.

Another disadvantage of late binding is that you introduce a new dependency — this time on the supporting infrastructure. Maybe you’re using a great SOAP toolkit for PHP and that toolkit makes it easy to late bind to a web service. But now you depend on your SOAP toolkit. What if another actor in your system doesn’t have the same version of the toolkit?

What we’d like is a mechanism that combines the predictability and robust tool support of the traditional approach to interface versioning with the flexibility of late binding to get the best of both worlds. In part 2 of this series, I’ll look at some approaches to that goal, and discuss why they still leave me unsatisfied. In part 3, I’ll offer my own solution.

Posted by: Daniel | June 25, 2008

Why People Are Part of A Software Architecture

I’ve been reading an interesting book — Beyond Software Architecture, by Luke Hohmann. In his first chapter he discusses the more people- and business-related aspects of architecture, and makes the following points:

  • Systems are designed to manage people dependencies, not just esoteric “code” dependencies.
  • Systems are designed by people with non-technical motivations.

Point well taken.

I wanted to chime in with one additional observation. An architecture and the people who actively develop and maintain it are difficult to separate. I’d argue that a company that owns the source code of a so-called “architecture” but that has no engineer that understands it doesn’t really have an architecture — as with Schrödinger’s cat, the human observer matters.

A stark example of this has cropped up several times as I’ve done M&A work during my career. I have seen less technical executives repeatedly assume that the source code of a system = the system. In other words, they think people are fungible and an architecture can be purchased and transferred *only* by shifting source. In practice, that assumption causes problems. The boundaries around modules don’t always exist cleanly in files and directories on disk; sometimes they are more of a mental construct in the minds of those who build and service them. If you grab a wad of source and plop it down in front of people who’ve never worked with it before, the institutional knowledge that contextualized the system in the minds of its creators is missing. This is why a mediocre engineer well-versed in the context of a given architecture is likely to be more productive than a world-class engineer dropped into the architecture cold. It is also why layoffs and RIFs are more dangerous than you’d guess by simply looking at raw staffing numbers.

Categories