Portfolio: Web apps, SaaS, and GIS
Blackbaud Grantmaking Project Lead
As project lead on an 18-month rewrite of Blackbaud, Inc's Grantmaking platform, an approximately $30 mil/year revenue product, I was tasked with updating a legacy CSLA.NET based application whose client-side was mired in a Silverlight-dependent presentation stack.
Prior to my arrival, Blackbaud had begun a complete rewrite of the Grantmaking system using their own Azure-hosted .NET MVC microservice stack utilizing Angular 2+ and Mongo DB, but their attempts had missed several deadlines, including Silverlight's deprecation by Microsoft in 2012 and its loss of support in every major browser other than the latest versions of Internet Explorer on Windows. Competitors were selling against the stack, saying that we had no plans to support clients in the future.
After getting the lay of the land, I proposed a new MVP focus to ensure clients would have a new system to use before Silverlight's October 2021 end of life date. The key points for this new focus were:
- Keep and maintain the legacy system's mature and complex business logic, written in C# and SQL Server using the CSLA framework.
- Create new Azure cloud microservices in the Blackbaud ecosystem using .NET MVC and MongoDB to...
- Act as a secure proxy to the legacy system's API
- Support Blackbaud Single Sign-On and support inter-service interoperability/upsells.
- Use an 80/20 Rule mentality to port the existing Silverlight UI to Angular 6, leveraging the existing UI as a "high fidelity prototype" rather than reconceiving the entire system.
A short time after presenting this plan, I was shifted from team lead to project lead. I worked closely with product management to define strategy, make tech stack decisions, create the backlog (from the epic, feature, and milestone macro to Scrum story micro), and set development priorities for the four development teams. I also continued as a key hands-on contributor, coding about 50% of each working day to demonstrate best practices, review new code, and complete complex tasks.
Within a few months, the newly architected system was running well enough to allow entity entry -- something the preceding rewrite had not supported even after four years of development (no kidding!).
QuestionBuilder

I was hired to help modernize Hawkes Learning's stack, initially with an emphasis on the client side. The stack was an amazing working museum of Microsoft technologies. The most recent techs were .NET MVC with some AngularJS (v1.8!) on the client, but the oldest were Classic ASP pages written in vbscript straight from before the turn of the century and VB6 dlls still in production! The majority of the company's systems were in one monolithic Visual Studio solution, and builds were nasty.
It had been decades since I'd written Response.Write strTitle & " " & strFirstName
. I had my work cut out.
Question Builder is Hawkes' SaaS tool for college instructors to create exams for students with online scoring, remediation, and training. My first goal was to refactor Question Builder code to pull out closely-coupled .NET MVC controllers with OWIN authentication and put into microservice-ready ASP.NET Core APIs with JWT-based auth. Now the middleware was client- and host-agnostic; we could access via React, iOS, anything and start hosting the Question Builder services on Azure easily if we wanted.
We then researched, documented, and introduced approximately five development teams to a Vite-powered client stack built on React, best practices enforced by ESLint, and testing via vitest. I also created a transpilation-free, Preact-powered client stack to enable minimally invasive but modern client development within the company's dated jQuery-powered Classic ASP and AngularJS code without requiring complete developer head rethreads and without touching antiquated build and deploy processes, which was a chief concern for the company's CIO.
Before this could get started, we also ate some storypoints designing a complex set of instructor permission rules for who could use and/or edit specific questions. An admittedly simple UI state for this work can be seen in the Permissions screenshot, below, but you can easily imagine from the UI the complex combinations that featureset engenders.
Additional screenshots
- Assignments Landing Page
- Question Entry
- Variable Manager
- Permissions
- All Assignments Grid
- GraphBuilder
MessageFactory
MessageFactory is a .NET MVC system that facilitates the surprisingly complicated process of laying out direct mailings for one of the country's leading "full service direct marketing" companies, who serves several Fortunate 500 companies. It seeks to replace arcane, in-house systems of emailed Word documents, shared folders of dated image files, and complicated management structures with a streamlined system that handles asset management and versioning, layout composition, and automatic manager notifications.
Work started when the system was still in its relative infancy, as a team of two developers (one in-house and myself as a contractor/consultant) scaffolded the system from scratch. I worked mostly on three areas, the original job edit screens, asset management pages, and then the notifications system (for which I was solely responsible, schema to client) over the course of just over a year. All sections used Telerik web UI controls (except administration pages), .NET MVC, and LINQ to Entities to talk with SQL Server.
Other responsibilities included creating a plugin for the ckEditor web editor plugin and the use of a zero-dependency JavaScript tag UI widget I wrote over a break, tagifyJS.
Additional screenshots, in png format:
- Job Info Edit Page
- Add/Edit Asset Pop-up
- Notifications Settings Page -- User
- Notifications Settings Page -- Admin
- Notifications Service (as console)
HIPAA Secure Chat
Secure chat was a project that required the creation of a system-wide chat system to backfill into an existing healthcare practice management SaaS. Beyond simply integrating with the current code, a two-headed client with legacy KnockoutJS code partially ported to ReactJS, the new chat system also had to keep HIPAA-compliant privacy and auditability requirements in mind.
The client UI was reasonably straightforward. The stack was ReactJS, MobX, and JSX, transpiled by Babel. There was a dedicated chat interface that was a standard module, managed by a homegrown module router for React that could be coded conventionally. The pop-up UI that needed to appear within the top-level menu throughout the application, however, required creating and managing a new MobX datastore to ensure all chat data was available even in sections that had not been ported from KnockoutJS. But, again, reasonably straightforward.

The server side was a bit more convoluted. Before I started, the client had licensed with an XMPP provider to power the chat system. Though the XMPP provider, QuickBlox, did support secure AWS hosting and excellent auditability (all messages were saved and backed up nicely), XMPP remains more suited to Trillian-style Internet chat from the early aughts than secure HIPAA messaging in 2018. The minimally invasive solution was to block outside access to QuickBlox' messaging endpoint, and handle all sends via custom proxy middleware that would integrate with the practice management software's existing rules, roles, and rights. Within this middleware, user logins could be assured, and messages forwarded to the XMPP server only when sent to recipients who at that moment had valid relationships.
There were a number of edge cases to cover, as when group permissions might change, and a group "host" lost their relationship/permission with a group member (or vice versa, when a member lost their relationship with the chat group host). To ensure history wasn't lost to any group member, such situations required freezing the conversation, preserving access, but preventing any new messages from being sent until all group members regained active relationships with the group host.
QuickBlox itself also provided some issues, with unexpectedly high resource use on its AWS instances that required reboots and with login and command throttling when accessed from specific IP addresses, like the custom middleware that had to proxy sending all users' messages.
The server-side proxy was refactored to optimize login management to account for these issues, providing a service solid enough that the SaaS company itself used the new chat service as an in-house replacement for Slack after just a few months of development.
Additional screenshots, in png format:
- Dedicated module UI with profile card showing
- Pop-up UI with Start a Chat displayed
- Pop-up UI only
- Responsive design on 5" Android
PeopleMatter
PeopleMatter was a SaaS solution for hiring, training, and scheduling applicants
and employees who work in the services sector, since purchased by Snagajob.
Work for PeopleMatter centered on two pieces of the product: Schedule, the module that creates schedules for employees and different stores, and maintains a running count of their hours worked and costs to staff, and Hire, where applicants to companies are tracked, interviews scheduled, and onboarding tasks (I-9 forms, workforce eligibility, training) can be managed.
Schedule work was done using .NET MVC with C#, SQL Server via NHibernate on the backend, and KnockoutJS templating for the client. It initially was a seven team [sic] project that leaned exceptionally forcefully on maximizing data on the client's browser -- the JSON payload routinuely went over a megabyte when testing medium-size sample companies. Similarly, the client contained especially complex JavaScript view models bound to KnockoutJS templates to manage that data. Work here centered largely on client validation, ensuring proper translation of business rules from server to client code, speed optimizations, memory management, AJAX interactions with server controller actions, and data serialization.
My most important work on Schedule was probably when, a few weeks from a contracted release, I was given the task to look at performance on Internet Explorer 6. Schedule was using infinite scroll, where scrolling down the schedule would continually load more entries into the UI. This worked well in Chrome, especially on faster machines with recent processors, but on slower machines -- or any machine using Internet Explorer 6 -- performance for sizeable schedules would quickly crater.
After letting management know, "It's dead, Jim," I took a few weeks to work with a designer and non-destructively add paging to Schedule for Internet Explorer 6 and 7 only. It was an exceptionally defensive fix, could be turned off by changing one boolean in the client side code if we chose not to use paging, and could change the number of entries on a page by changing one integer, even pitching different numbers of entries for different browser versions and types. This was a minimal risk fix with a potentially high reward.
Since all of the data for each schedule was downloaded in a giant, originally single-use JSON payload on page load, it was only necessary to add a de/serialization routine to the JavaScript code for a "person-schedule", the foundational object model used on the page, to and from that JSON payload. As pages made edits saved to the server, those changes to person-schedules were serialized into and out of the client-side JSON datastore so that fresh information was accessed during paging, allowing slower browsers to make the same edits with much less DOM overhead than the original design required -- and to do so without any changes or new interactions with the server. This did duplicate server-side serialization logic, and was a band-aid on a much more serious performance issue (and an architecture that didn't scale as well as was needed), but a few weeks of triage work, done without any server-side churn, allowed Schedule to ship on time to all users, particularly to a major "homepage logo client" whose internal network only allowed the use of IE6.
There is a pretty good training video that demonstrates in Schedule in less than two minutes here, with a local zipped copy in case that link ever breaks.
The Spring 2014 release concentrated on Hire. To help make changes in applicant status auditable, we added a new "Status Change Reason" interface. Each location in a company could optionally turn on this audit function which would, for instance, collect reasons when one applicant was promote to a job candidate, or had an interview scheduled, and would also capture when an applicant for a job had been rejected. Now, instead of simply having a note of when a change was made, managers could look back and now why those changes were made and defend the decisions if audited for age or other demographic. This work took on quite a bit more refactoring than it might appear. Each status previous had its own separate workflow in the code, and this work largerly pushed them all into a shared process that reused dialogs and centralized tech debt heavy code.
Save for Later was a much easier task, and simply added a new searchable category for applicants. It was essentially a bookmarking feature.
Additional screenshots, in png format:
- Save for Later and Status Change Reasons with description
- Training video for Schedule (local zipped copy)
- Schedule video for "Using Thresholds"
Army Mapper
Worked on the "Development 1" task of Army Mapper 3.0, the US Army's enterprise Geographic Information System (GIS), used for a number of functions, including reviewing the utilization of real property (buildings, roads, parking lots) and other infrastructure on U.S. Army bases. Army Mapper is used by the Office of the Assistant Chief of Staff for Installation Management (OACSIM) as a tool to visualize the current state of the Army's resource management across Army installations (ie, bases) worldwide.
Work in the Development 1 task included designing and creating exceptionally advanced, user-facing geo/database query tools, allowing Common Access Card (CAC) authenticated end users to visually construct and save ad hoc queries of what amounted to complex SQL joins. These joined data sources, cutting across any number of tables, could then be concurrently queried by data values and geospatially. Other tasks included creating a Range Query Tool (RQT) that allows Army support personnel to quickly find firing ranges by munition or weapon type, or by querying a number of other range characteristics.
Army Mapper 3.0 employees MapFish and MapServer to interface with spatially aware and flat data in Oracle and ESRI ArcSDE. Work was done in PL/SQL, C#, Javascript, and ExtJS.
Recent internet map service programming has taken a clean break from the ESRI-specific solutions to which I contributed while at NOAA CSC. Army Mapper, the US
Army's enterprise Geographic Information System. This system uses ESRI, Oracle Spatial, and open source libraries to provide its functionality, including MapServer, OpenLayers, and ExtJS.
DHEC Immunizations Registry

This system did have fairly complicated business rules, like figuring out how many days old a baby is (three months from June-August isn't the same number of days as January through March, and some shots counted one way and some, apparently, another!) and how long after an initial shot could another be administered and still be effective. HIPAA considerations also meant you had to give pessimistically constructed searches. Accidentally giving out every Smith in the database would be a really bad, and potentially illegal, thing to accidentally do.
Note that the relatively tight width and plain design was, in part, due to conservative rules about users' machine, ensuring that fairly old browsers and even older hardware would still be able to access the system easily. The interface and data tier were written in the space of approximately two and a half months (ramp-up included).
Additional screenshots, in png format:
SpiderSavings.com

This project involved creating a new database schema from scratch, allowing image manipulation on the server to create and design coupons, accept web payments, produce and send SMS messages, and use AJAX to provide thick functionality within a browser.
Additional screenshots, in png format:
- Most Recent Coupon Listing
- Example Coupon
- Example Coupon with YouTube Embed
- Coupon Provider Home Page
- Administrator Provider Review Page
- Admin Banner Ad Review Page
Historical Hurricane Tracks Tools

To date, the most popular site has been the Historical Hurricane Tracks Tool, which allows users to query a system that includes over one-hundred years of tropical storms on the East Coast of the United States to see what storms might have passed through their neighborhood. This site was highlighted at USAToday.com in a weather article. An in-depth consideration of the technologies used was given as part of a presentation at the 2002 ESRI Users' Conference, summarized in this abstract.
RAster Data Delivery System

Links to site descriptions
Historical Hurricane Tracks Tool
Coastal Services Center Management Information System (MIS)

Additional screenshots, in png format:
- Front Page
- General Project Information
- Project Personnel
- Milestone Edit
- Project Review
- Milestone Report
Information Request Tracking System (IRTS)

Additional screenshots, in png format:
CSC Product/Project Description (PPD) Maintenance System

One major advantage of the system was that the database template was passed through the normal review channels for web design, ensuring the Center-wide, mandated look and feel was utilized, but new products could be added without additional, intra-office review.
Administration page mock-ups (uneditable; for viewing only)
NCSU CRDM Students Page

Description of development process for NCSU CRDM student information page
Description Page