Be a Better Programmer, Part 4

Aug 14, 2014, 9:44 AM

Tags: abstraction
  1. Be a Better Programmer, Part 1
  2. Be a Better Programmer, Part 2
  3. Be a Better Programmer, Part 3
  4. Be a Better Programmer, Part 4
  5. Be a Better Programmer, Part 5

This topic isn't, strictly speaking, in the same vein as the rest of this series; instead, it's more of a meandering "case study" sort of thing. But it has something of a unifying theme, if you'll bear with me:

Be Mindful of Your Layer of Abstraction

Basically everything about computers has to do with layers of abstraction, but what I have in mind at the moment is how it interacts with web programming. First, I'll dive into a bit of background.

The original premise of HTML was that it was an SGML-like markup language designed for representing textual documents, particularly academic ones. Thus, the early versions built up a bevy of elements for representing structured tabular data, abbreviations, block quotations, definition lists, ordered and unordered lists, and so forth. The ideal HTML document would be structurally sound, with each element meaning something that both a human and computer could understand and, presumably, format and put into a textbook.

However, since actual humans don't spend all their time writing academic papers and instead wanted to make layouts and colors and fonts, the HTML they put out wasn't beautifully structured - instead, tables went from representing tabular data to being used for layout, filled with little 1px-by-1px transparent GIFs and other semantically-meaningless crap.

Enter the Web Standards Project, which primarily advocated the use of cleaner (X)HTML and CSS. Among the goals of this advocacy was a desire to see cleaner, more meaningful markup again. This was phenomenally successful and, as browsers improved their support of CSS (grudgingly, in some cases), the miasma of nested tables, font tags, and other meaningless markup receded.

But then we get to today. Though modern HTML markup bears the trappings of "semantic" standards, the essence of it is hardly better than the old way (h/t David Leedy). The proliferation of framework-geared markup is a reflection of the fact that, even with the best of intentions, sticking to strictly meaningful markup is essentially impractical for an "app"-type web site (as opposed to a collection of documents). When you get into the HTML that various JavaScript tools generate at runtime, things just completely fly out the window. HTML5's new elements help a little, but don't solve the problem - is a dashboard page containing a grid of widgets really better represented by <article>s and <section>s than by <div>s? The equillibrium that "best practice" has reached now is that it's more or less okay to have a bunch of nonsense <div>s mucking up the works as long as you try to do a best match for actual content tags and throw in some nods to accessibility/inclusion.

So that brings me to my current views on how to structure the markup of web apps. I used to care a great deal, back when I wrote in PHP - since every tag on the page was something I wrote, either hard-coded or in HTML-generation code, I wanted to make sure that the result was pristine and would work properly even with CSS and JavaScript turned off. My early XPages attempts tried to continue with this, but it's not realistic. While you can (and should) still keep an eye on not spitting out too much HTML unnecessarily, there's no getting around the fact that your page is going to have more <div>s and <span>s than you'd like.

Over the past year, my view has shifted instead to the notion that the resultant HTML+CSS is basically compiled output - or, more accurately, is equivalent to the actual pixels and widgets pushed to the screen when you run a desktop app. In a desktop app, you write to the UI frameworks of the platform and then the frameworks do what they need to do to render the window - sometimes they'll use a different user-specified font, sometimes it'll be larger or smaller, sometimes it'll be high-contrast, etc.. Similarly, my view of an XPage's HTML is that my job is to make the XSP code as clean and declarative as possible and then it's the server's job to decide how that's supposed to be represented in HTML. The ideal XPage wouldn't contain a single nod to the fact that it's going to eventually be output as HTML.

This point of view has come alongside (or because of) my getting religion on custom renderers. Now that my best-case development strategy revolves around renderers, it feels wildly inappropriate to include any references to HTML or CSS classes inside XSP markup (or even themeIds to indirectly reference Bootstrap layout concerns). All that stuff should be determined by the framework at runtime (by way of the renderers and component-generation code), and the XSP should focus on defining the grouping and ordering of the primary components of the page.

But the trouble is that all these abstractions - from pseudo-semantic HTML, through the CSS framework, through the XSP markup - are terribly leaky. No matter how clean I try to make my XSP, there's no getting around the fact that a well-crafted layout needs some knowledge of, say, Bootstrap's column-layout model, or that I want all the containers referencing Task properties to be red instead of the default color scheme. And no matter how clever the UI framework is with its CSS trickery, at some point you're going to have to put in some extra layers of <div>s to get the right sort of alignment (or, worse, include a set of empty <div>s just to get that hamburger icon). And who even knows how you're supposed to feel about abstraction once you start using JavaScript frameworks that generate the entire page - or go even further? Those tend to lead to clean data feeds, so that's good, but phew.

So it's all rules of thumb! The best tack I've found is to pick a layer of abstraction that makes sense with my mental model - standard XSP components + renderers in this case, but it applies to other areas - but to still know about the lower layers, because you're going to have to dive into them sooner or later. That's not, strictly speaking, practical - the XSP framework itself is built on so many other interlocking parts that it's effectively impossible to master every layer (XSP, HTML+CSS+JS, JSF, JSP's leftovers, Java servlets, OSGi, Java, the Domino API, Domino-the-platform, the OS itself, and so forth). But you can know enough generally, and taking the time to do so is crucial.

In short: the whole thing is a mess, so just try to keep your head clear and your code clean.

Commenter Photo

Nathan T. Freeman - Aug 14, 2014, 5:52 PM

"...standard XSP components + renderers in this case..."

So close... but the standard XSP components are the legacy holding you back. InputText vs. Combobox vs. Checkbox vs. Textarea... Repeat vs. ViewPanel vs. DataTable vs. DojoGrid... Inputs that need Labels, Converters and Validators as separate components... these are refugees from Notes development that, even with custom renderers, still lock your thinking into static trees.

You've touched on the way out of this with your bean-binding approach, and you know I love it. But if you had your own components, you could stop hacking around the limited attributes that IBM has granted to you and open up a new world for your models.

The cool thing is: you'd only need about 5 or 6 components, and your custom renderers would do the rest.

Commenter Photo

Jesse Gallagher - Aug 14, 2014, 5:58 PM

I'm definitely dancing around that sort of thing, but I'm doing it deliberately slowly. If I go in assuming that I should make my own components for most things, I'll likely end up reinventing wheels and over-engineering - instead, I'm trying to use the stock+ExtLib controls as much as possible to feel where the real limits I run into are, and then eventually I'll replace what's required. The stuff I did with auto-generating input components when attached to model objects is an exercise in this: the formTable and formRow seem "close enough" so far to what I need, and then the Java code generates the correct input controls, which I could eventually write myself without worrying about adding them manually to XPages.

I'm almost definitely going to have to do something about layout, though - there's no good way to express a responsive layout with the existing controls, and trying to gussy up divs or panels is no good.

Commenter Photo

Nathan T. Freeman - Aug 14, 2014, 9:24 PM

"If I go in assuming that I should make my own components for most things..."

No no. Don't make your own components for most things. Make your own components for just a few things. Then make them adaptable enough that you only need those few things.

<frost:formPanel model="" var="Task">
 <xp:repeat value="#{Task.inputs}">

All the rest is renderers.

Commenter Photo

Jesse Gallagher - Aug 14, 2014, 9:58 PM

I know that that example isn't the end-all-be-all of your point, but it's actually a good example of a case where I ended up fine not creating a new component for my needs. With the component binding stuff I have now, I can do something like this:

<xe:formTable binding="#{controller.components[task].all}"/>

...which then tells the controller to fill in the appropriate formRows and controls for all of the defined properties of the model object. So the effect is similar to defining my own components, but it shifts the responsibility to the controller, which involves less code overall.

And don't get me wrong - I expect I'll codify the various CCs I often use into proper controls as they prove their lasting value (name pickers, for one). I just want to avoid the nightmare scenario where I disappear up my own petard creating arcane combinations of specialized controls and themeId combinations when I should instead look through the standard lib more and adapt them with some contextual processing.

Commenter Photo

Nathan T. Freeman - Aug 14, 2014, 10:35 PM

Argh! It's like I'm not even talking! :)

"...creating arcane combinations of specialized controls and themeId combinations..."

Yes, *exactly*. Don't create specialized controls. Create generalized controls for your XSP and create specialized renderers.

For example, do not ever, EVER turn your name picker into a component. Picking a name should not be a component. It should be a data type for your model. The component should just be "input" that is dynamically bound to an element in your model. When that element has a property of "name" then the renderer can say "okay, I should put a button next to the <input> tag that brings up a Dojo dialog box that looks to the directory source defined for my environment or Java class."

That way, 6 months later when you have the revelation that a name picker isn't fundamentally different from any other typeahead select control, and that it should be a Select2 that knows how to render a "contact list" complete with avatars from a JSON-RPC request, *you don't have to change any XPages*. You just change the renderer for your input that branches when the model defines a datatype of "name". Replicate in a single update site and you're done.

Not more components; fewer. Just a handful of them: input, output, list, grid and grouping. Everything else should be decided by the data being rendered and the requesting client.

Commenter Photo

Jesse Gallagher - Aug 14, 2014, 10:53 PM

We are overall talking about the same basic thing; the difference is that I ended up coming at the problem from a slightly different direction (bindings to controllers) while also exploring the utility of renderers. So the hypothetical "name picker" control isn't something that would actually ever be in XSP markup: it'd either be added by the controller or obviated entirely by the controller building a collection of existing components to get equivalent behavior.

It's the layout-related controls like grids and grouping where I'm most likely to create controls that I also use in XSP markup, since those are taxing the abstraction layer the most. I've gotten good milage out of applicationLayout, widgetContainer, and formTable, but they can only go so far. But since those are an area where I've seen (and written) some of the crummiest custom controls and idioms, I'm still in the "see what I need long-term, then write something for that" phase.

New Comment