Skip to main content

Project in Review - Part 3: What didn't work

Of course, not everything was an unmitigated success. I tried many things that didn't work out. Much of which I've removed and forgotten about, but a few things remain - either scarred into my psyche or woven too deeply to fix.

What didn't work


Storing my entire configuration in application.properties

Using properties files is great. It let me get configuration out of a profile document and into something much easier to edit - particularly configuration that users will never see or maintain (and thus there is no need for an interface for). But I took it too far. The paths to the other databases are there, and that's good. But view aliases are also there, and that was a mistake. I already have a ViewDefinition enum that describes each view and all the information I need to know about it. I could have set view names there, but instead I'm reading them from the application config. I can change where a view is pointing without having to go into my code. Except of course, the view I want to get is only ever going to change when there is a design change. I avoided hard-coding view names by adding another unnecessary layer of lookups. It works fine, but adds nothing of value.

Writing my own DocumentBean - maybe?

I'm going to plead initial ignorance on this. I didn't know about DominoDocument, so I began this project by following a tutorial to create a very simple DAO bean that just magically worked! And it did, too! For so long that by the time I realized it wasn't working for me, it was baked too deeply into my application to remove (which, if I'm honest, probably points at a significant architectural flaw). So now I have a Frankenstein's Monster of value wrappers and converters and config files to define field types. The result is solid, but it is technical debt and cost me a fair bit of time as I had a number of false-starts.

Would I have run into a different kind of problem DominoDocument? Possibly.

"Universal" custom controls

One of my colleagues working on this project was not as comfortable with Java or Bootstrap or jQuery as I was. So I tried to write some controls that were drag-drop-enter some params. I also tried creating some bootstrap row and column controls so that layout could be a drag-and-drop thing. But I just couldn't get it to work. It probably would have been fine if I wrote it like a native control, but I tried to piece it together out of existing components and it became unworkable. I can't recall the specifics (and I should re-discover them for a future post) but I think it had to do with order of object instantiation and when properties were available

Some controls worked out well, when they were fairly self-contained, but my attempt to make building an application as easy as snapping legos together was a failure, and I spent way more time and effort trying to make it work than it would ever have taken to just write out all the XML - which in the end replaced 95% of these universal controls.

Themes

Going into this project, I thought themes were great. I used them for everything. All my textInput controls automatically added the 'form-control' class. I had themes that defined 4 different sizes of row div so that Bootstrap could work it's magic and make everything line up and overflow down and all that good stuff. It started to fall apart when some things needed computed styleClasses (or other properties) but also needed the values from the theme. Oh, and sometimes sometimes hard-coded into the XML. Most of the time, this would at least work onPageLoad... but not always. And it frequently broke in partial refresh. Worse, it had me doing stupid things with my styling in order to limit the number of ThemeIds and to keep some distinction between them and a simple CSS class. There is still lots of theming in my application, but many things are either set in the XML or entirely through a bean and bypass the theme altogether.

Bootstrap

Bootstrap was an integral and successful part of this application. However, I ran into a bunch of difficulties that left me questioning, at times, if I should have just created a stripped-down version. The overriding theme is that the learning curve was larger than I'd initially expected. Also, I think we adopted Bootstrap 4 too early in the beta cycle.
  • I had to create a standard that a layout callback control used (or was contained in) a 'row' div - I created a mess for myself before that.
  • My familiarity with when and where to use things like 'btn-group' or 'form-group' or another layer of 'row' and 'col' was rapidly evolving. And due to universal controls not working as I'd liked, my components are frustratingly inconsistent. Months into development, I had to move from a [label]:[field] layout to a label over field layout because I was tired of constantly fighting against how Bootstrap was trying to do things.
  • I created many custom rules to "fix" something I thought was wrong, I wound up breaking something else. Sometimes it wasn't clear until I was far down a rabbit hole of changes that my solution was worse than just hard-coding a style. It was difficult to tell when something was a bug, or if I was fighting against Bootstrap.
  • It took me a while to realize I needed to set Text.Label and Text.ComputedField to 'form-control-static'. I also had to create a version that added display:inline-block. And then, because not all of my app got moved from left-right field layout to over-under, I still had to do a lot of fiddly things and assign themes to get it all to work.
  • Only tangentially related to Bootstrap, the documentation indicates that Typeahead.js is the way to do a type-ahead control with Bootstrap. It probably was inherited from Bootstrap 3 - I don't find any current mention of it in conjunction with Bootstrap 4. But it turns out that the official Typeahead.js project has been abandoned, but there is a separate fork maintained by the community. So I struggled for too long thinking I was doing something wrong when the problem was a broken JS library. Then once I figured that out, I again had to struggle too long to get it to read the output from a view REST service. Ultimately, this looks and works great, but it was a lot more time consuming than I'd anticipated.
  • I had to write a custom pager renderer for Bootstrap. It wasn't exceptionally difficult, but I wouldn't say it is for the faint-of-heart, either. I actually wrote a number of custom renderers, but I prefer not to write code where there is another solution, and in all other cases I was able to find solutions that didn't require the renderer. Developing renderers can be tricky because they tend to either blow up spectacularly, or fail completely silently. I'm sure there are techniques to help with this, but I didn't spend quite enough time to figure them out.
Overall, Bootstrap was a success, but it would have been far better to have more time to develop tooling like native controls and custom renderers. Trying to integrate Bootstrap with the output from IBM's renderers was painful.

Parallel interface

Developing my application as a 'skin' on top of the existing database remains a success I hope will be proven over time. But there were a few things I inherited that are just bad design

As an example, there is a particular keyword document and view. Here is how it works: the document has a Title field, plus Level1Title, Level2Title, Level3Title, Level4Title and ChildCount. There is no parent-child relationship. A document with only a Title is defacto a Level1Title for anything under it. There is a process for updating the ChildCount. This results in a categorized view with possibly several empty categories. This was a bear to create a decent interface for. I would have redesigned that from the ground up if I didn't have to keep the legacy interface in place.

Comments

Popular posts from this blog

Pass data between XPages and existing LS scripts

I'm working on modernizing a fairly hefty application with a lot of existing script libraries which we want to leverage within the XPages environment. Here is a technique that works very well. First, create an in-memory document in SSJS. We can set any input values needed for the back end. Then we pass that document to a LS Agent which can work it's magic using the values set in SSJS and use the same document to return values back to the XPage. Here is how it works in detail:

Quick tip: Convert a number to String in EL

I just had a need to do this and a Google search didn't immediately turn up a solution. So I thought for a couple of minutes and came up with this: value="0#{numberVar}" This takes advantage of the way Java auto-converts objects to strings when doing a concatenation. So if your number is 13, Java EL turns this into new String("0"+13), which becomes "013". You can then strip off the leading zero or just parse the string back into a number.