If you're reading this page then you've either applied for a position on our product team, or you're thinking about it. Welcome!
The purpose of this page is to provide detailed information about our technical stack and development environment. These details will, obviously, have a big impact on what day-to-day life on our team is like and it's only natural that you'd want to know about them in advance.
One note before we continue: software is an ever-changing field and some of these details may have changed by the time you read this!
Development Process
We like to think of ourselves as "small 'a'" agile. We believe in the core tenets of the Agile Manifesto but are not strict followers of any specific methodology, and we shift our process based on business needs.
Sometimes we follow a regular release cadence, using a form of Kanban to track work.
Other times we work in two-week iterations, influenced by Scrum.
In all cases, we believe that cross-functional teams, working in collaborative environments and with short feedback cycles, is the key to building quality software and maintaining a stable velocity over time.
Programming Languages / Platforms / Architectures
LearningBuilder is a large, reasonably well-organized monolith consisting of a handful of core assemblies plus the web project. Our long-term vision is less monolithic and more a federation of independent services working together. We have adopted Vue.js for new UI features and have migrated two major features to it so far.
We use:
- ASP.NET MVC (exploring a .NET 6 migration in 2022)
- Vue.js
- SQL Server 2016+
- SQL Server Service Broker / RabbitMQ, for messaging
- ElasticSearch, for publicly-available search features
- SASS, to take some pain out of CSS
- Ruby and Powershell, to automate our development process
- AWS for deployment, with growing use of Lambda functions for custom integrations
Developers run the full stack locally.
Data Access
All database schema changes are fully scripted (using a homegrown utility) and are source-controlled like everything else. We maintain a shared database snapshot in a known good state so that you're only ever one command-line tool away from restoring a local database with staged data.
Our legacy features use an ORM called NHibernate to interact with the database. Queries are typically expressed using the NH LINQ provider, but when necessary we'll hand-roll a query to optimize performance. Newer features use Vue.js.
We also work with stored procedures a decent amount; LearningBuilder is a very configurable product, and sprocs are often used as a way of injecting client-specific business logic into pre-defined "hooks" in the app.
Automated Testing
Automated testing is a big deal on our team. We switch between TDD, "test-first", and "test-after" workflows depending on the nature of the changes we're making. Regardless, the goals are always to:
- Prove our code does what we expect it does
- Document our intent to aid other programmers
- Prevent regressions and aid in issue triage
As of this writing we have many thousands of unit tests and many hundreds of automated UI tests. These tests can be executed via the command line, via the NUnit test runner, or within Visual Studio using R# or TestDriven.NET.
We have invested heavily in a library of "data helpers" that assist with setting up test data, ensuring that it remains fairly easy to write new automated tests even as the product complexity continues to grow.
Source Control / CI
We use Git and Bitbucket.
Our general pattern is that most tasks are performed in feature branches and then merged into trunk once they are stable and are code-reviewed. We maintain release branches so that critical changes can be merged across multiple lines of development if needed.
The entire test suite is automatically executed for each check-in to trunk, and we will often set up CI builds to monitor long-running feature or release branches as well.
Deployments
The process of preparing a release package is fully automated using a Ruby tool called Rake. Rake scripts will do a fresh checkout, compile the release package, run the tests, commit the package to source control, etc.
Deployments themselves are handled through Octopus Deploy and are approximately 75% automated. Our IT/Devops team manages the deployment process.
Technical Principles
In addition to the core corporate values (outlined below), the technical team believes in the following:
- "Go slow to go fast". We are interested in long-term stable velocity over time. We achieve that by paying attention to fundamentals, writing clean code, and sweating the details.
- Diversity of thought / consistency of output. We value different ideas and problem solving strategies, but believe that consistently-applied patterns helps us manage complexity.
- Always Build Components. We like to build product features on top of reusable components, patterns and frameworks that minimize and clarify the feature-specific code. We invest in helpers, wrappers, utilities, etc to eliminate as much boilerplate and repetition as possible.
- Automate all the things - ain't nobody got time to be doing repetitive tasks by hand!
- Innovate deliberately and intentionally. We all love shiny new stuff, but not every task is the right time to introduce brand new ways of doing things. But when it is the right time, then look for the evolutionary leaps and not the incremental shifts.
- "It's better to build half a feature than a half-assed feature." (See "go slow to go fast")
- Scope is negotiable; cleanliness is not. Nothing is more important than writing clean code, that clearly expresses its intent, and is as simple as it can be, because that is the foundation upon which all future progress will be made. (See "go slow to go fast")
Core Values
Heuristic Solutions has the following core values:
- Pioneering - success often means heading out into the unknown. But don't just take risks willy-nilly; take calculated risks.
- Act intentionally - make decisions about what you do. Think heuristically. Anticipate the future. Leave breadcrumbs.
- Achieve excellence with others - teamwork matters. Engage others. Crave accountability.
- Show that you care - companies are made of people, not "resources". We build strong teams by caring about each other as people first, and coworkers second.
These aren't just words on a motivational poster in the corner; they are things that we truly believe in, and you'll hear them frequently as we do our work.