Transparency is the currency of trust, the open sharing of information among parties involved. Often, a decline in transparency is the first indicator of trouble. As transparency declines, trust declines.
Companies struggle to keep everyone on the same page. People are hyper-connected in the moment but still don't know what's happening across the company. Employees and investors, co-founders and execs, customers and community, they all want more transparency. The solution is surprisingly simple and effective - great company updates that build transparency and alignment.
With that in mind we designed the Carrot software-as-a-service application, powered by the open source OpenCompany platform. The product design is based on three principles:
- It has to be easy or no one will play.
- The "big picture" should always be visible.
- Alignment is valuable beyond the team, too.
Carrot simplifies how key business information is shared with stakeholders to create alignment. When information about growth, finances, ownership and challenges is shared transparently, it inspires trust, new ideas and new levels of stakeholder engagement. Carrot makes it easy for founders to engage with employees and investors, creating alignment for everyone.
Carrot is GitHub for the rest of your company.
Transparency expectations are changing. Organizations need to change as well if they are going to attract and retain savvy employees and investors. Just as open source changed the way we build software, transparency changes how we build successful companies with information that is open, interactive, and always accessible. Carrot turns transparency into a competitive advantage.
To get started, head to: Carrot
Information for new OpenCompany platform developers:
- Technical Values
- Technical Priorities
- Process Priorities
- Architectural Priorities
- System Architecture
- Coding Guidelines
- Tools
- Code of Conduct
- License
KISS - Keep It Simple Stupid - Complexity is our enemy. We need to solve the complexity inherent in the problems we face using simple solutions, and avoid introducing complexity that isn't inherent. Solution complexity is rejected, rather than dealt with. We ask 5 whys about complexity. What can be relaxed or changed to make the complexity go away?
YAGNI - You Ain't Gonna Need It - This is the answer to much introduced complexity. We force complexity upon ourselves by being so good at asking "But what if...?" Instead, we build for the problems we have today, not for the problems we think we might have someday. None of us are fortune tellers. We're mostly wrong.
No Punting - No one owns an app, a system a section of code, or a problem. We don’t punt issues off to others just because we weren't involved in the inception. Everyone works on, and fixes everything. Not knowing how something works doesn't mean you don’t work on it or fix it, it means the opposite! We seek out every opportunity to work on things we don't know about (and we fill in any missing documentation as we learn). Everyone on the team is full-stack. No specialists.
- Simple & DRY with a single responsibility
- Simple - see KISS and YAGNI above.
- DRY - Don't Repeat Yourself - there's no place for copy and paste. It inevitably bites you in the ass.
- Single Responsibility - units of software do 1 thing well, and only 1 thing. This is fractal and applies to lines of code, functions, classes, services, systems, and apps.
- Transparent & manageable - logs, analytics, error reporting, separation of config from code, and real-time adjustments are good
By following these 2 top technical priorities, we get two important emergent properties: robustness and scalability
Our full set of technical priorities, in order:
- Simple & DRY with a single responsibility
- Transparent & manageable
- Secure
- Consistent
- Documented
- Tested
Developers are held accountable to these technical priorities. Code reviews are structured around these technical priorities.
Living these priorities requires hard thinking about the technical problems we face. Code that works is not the goal. Code that works by embodying these priorities, and the appropriate trade-offs between them, is the goal.
Production code gets tested. Tests run in continuous integration (CI). Not all code needs all kinds of tests (unit, integration, system, acceptance, ...). Pragmatism still rules and strict test-driven development (TDD) is not important to us (we prefer our own acronym of TDD, thinking-driven development), but automated test development is a core skill of all our developers and test automation is a key consideration in code reviews.
All services do their best to trap and report errors. Error handling and alerting is a key consideration in code reviews.
Deployment is automated. It's not done until it ships with automated deployment. It ships as soon as possible. Risk is mitigated by shipping small changes incrementally.
All developers write documentation. Documentation is part of code review: did the appropriate docs get created and updated for the addition/change?
Documentation is well-organized. Even when good docs exist, if the person that needs them doesn't know where they are or that they exist, they do no good.
Code is documented. Comments are good! It’s always critical to comment on why this code is doing what it's doing. It’s also important to comment on what the code is doing, when that's not completely obvious.
We build services, not monoliths. Services compose in architecturally sound ways to deliver services to layers higher up the stack.
Libraries are better than micro-frameworks, which are better than monolithic frameworks.
Observers are better than Pub/Sub, which is better than Queued Data Pipelines, which are better than Synchronous Services. Observers and queues are more robust than synchronous services (e.g. HTTP) and should be preferred where appropriate.
We use the right language for the job. We are a polyglot team. We prefer polyglot developers who have learned how to efficiently learn new things.
The right language for the job is generally obvious due to a language feature or library implemented in the language that makes the job a much easier match for our technical priorities. The job becomes much simpler, or more robust, or easier to test, etc.
If the job is general purpose utility computing with no language feature or library advantages, we use Clojure and ClojureScript as our default.
OpenCompany uses a distributed microservices architecture. Many services provide access to themselves via HTTPS and/or WSS APIs. Others provide their services through the SQS queuing service. A key linchpin is JWToken authentication tokens which are used to provide cross-service authentication and user state. A typical use of the service is through the OpenCompany web client.
Auth RethinkDB - RethinkDB data store of users
Auth Service - Authentication microservice, handles user authentication, Slack single sign-on, and JWToken generation (HTTPS)
AWS CloudFront - Content delivery network
AWS S3 - Simple Storage Service
AWS SES - Simple Email Service
AWS SNS/SQS - Simple Notification Service and Simple Queuing Service
Bot Service - Slack bot microservice, handles Slack integration other than single sign-on (HTTPS/SQS)
Change DynamoDB - DynamoDB store of change and see events
Change Service - Change tracking microservice, handles read/unread state per user and async notifications of new content (WSS)
Notify Service - Service that handles general purpose notifications to users and notifications for mentions in comments and posts.
Reminder Service - Service that supports scheduling reminders to users.
Email Service - Microservice for outbound content and transactional emails (SQS)
FileStack - File upload as a service
Google Sheets - Spreadsheets in the Google cloud
Interaction Service - Engagement microservice, handles user engagement with reactions and comments (HTTPS, WSS)
Proxy Service - Charting microservice, handles extracting charts from Google Sheets (HTTPS)
Search Service - Microservice for searching and indexing carrot data. (SQS and Elastic Search)
Slack Router - Microservice for receiving slack API events and publishing them to SNS.
Slack - Company chat application
Storage Service - Persistence microservice, handles information data management and access control (HTTPS)
Storage RethinkDB - RethinkDB data store of user content
Web UI - ClojureScript/React mobile and desktop web client
We're much more interested in ensuring code follows the values and priorities described above than that it follows any particular coding standard. Code formatting is just the small tip of a very large iceberg; poorly formatted code can be programmatically tidied, but poorly done thinking can't be programmatically rethought. That being said, here are some code guidelines we look for:
- Do your best to conform to the coding style that's already there in the repository... That means we like it.
- Use 2 soft spaces for indentation for white space irrelevant code/data. Use 4 soft spaces for white space relevant code/data.
- Don't leave trailing spaces after lines.
- Don't leave trailing new lines at the end of files.
- We prefer
defn-
to:^private
where its use is possible - Write comments. Comments are good.
- Write tests. Tests are good.
- Don't submit über pull requests, keep your changes focused and atomic.
- Submit pull requests to the
mainline
branch of repos. - Have fun!
The purpose of the pull request process is to ensure code and product quality. The reviewer has 2 important responsibilities: code quality via code review, and product quality via QA of the change, ensuring both that the new functionality or fix works as expected and that other related parts of the product haven't regressed.
- The creator of the pull request is responsible for adequately describing the change being made. The main components of the description are what changed, how it changed, and why it changed, and what steps were taken to fully QA the change.
- The creator of the pull request marks them-self as the assignee, which signifies they are the primary submitter of the change and will be the one to respond to reviewer's question, comments and requests for changes. If at some point another developer becomes the primary contributor of the change, the assignee is changed.
- At this point though, the PR is not yet ready for review, it may still be in progress. Once it is ready to be reviewed, the assignee adds the label "ready for review". In the unusual case that the assignee wants the PR reviewed by a specific developer, they can also assign that developer with the reviewer field. NB: This is not usually done and you need a good reason to want a specific reviewer.
- At this point, any other developer that sees the PR should do the review, assigning themself with the reviewer field, removing the "ready for review" label, and labeling it with "reviewing". The reviewer reviews the code and performs QA, repeating the steps provided in the PR description and thinking critically about what additional QA steps may have been missed by the assignee.
- Now the usual back and forth commences between the assignee and the reviewer consisting of questions, comments, ideas, requests, suggestions and changes. NB: Reviews are about the code, not about the people, as an assignee, try to remain objective and not take anything from the reviewer personally, and as a reviewer, be kind and stay focused on the code.
- Once the code is in a place that both the assignee and reviewer are happy with it, it is the assignees responsibility to merge the PR and get the code deployed into production. Any special notes on deployment steps or dependencies should be included in the PR for the reviewer.
If the job is general purpose utility computing with no language feature or library advantages, we use Clojure and ClojureScript as our default.
Clojure is a Lisp that targets the Java Virtual Machine (JVM).
Tutorials:
References:
- Clojure Distilled
- Cheatsheet
- ClojureDocs - a community-powered documentation and examples repository for Clojure
- Quickref for Clojure Core
- Clojure Quick Reference
- Clojure Codex - curated links
Libraries:
- A Directory of Clojure Libraries
- Clojure Libraries
- Luminus Useful Libraries for Clojure Web Development
- The Clojure Toolbox
Other:
- Leiningen - declarative dependency management and build tool (via plugins)
- Boot - functional build and dependency management tool
- Clojurians on Slack
- Clojure email list
ClojureScript is a Clojure compiler that targets JavaScript to run Clojure in the browser (or on server-side JavaScript interpreters).
Tutorials:
Reference:
- ClojureScript Cheatsheet
- ClojureScript Syntax in 15 minutes
- ClojureScript translations from JavaScript
Other:
- CLJSFiddle - try your ClojureScript online
- ClojureScript email list
- core.async - library for async communication, often used in ClojureScript (reference)
Rum is a ClojureScript interface to React.
References:
Libraries:
- Derivatives - library for deriving needed component state from state atom
Other:
RethinkDB is an open-source, multi-modal (document, key/value, relational) database.
Tutorials:
Reference:
Other:
- clj-rethinkdb - a RethinkDB client for Clojure
Elasticsearch is an open-source, distributed, JSON-based search and analytics engine designed for horizontal scalability, maximum reliability, and easy management.
References:
Libraries:
- http://clojureelasticsearch.info/ - an Elasticsearch client for Clojure
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
Developer Onboarding by OpenCompany, LLC. is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Copyright © 2015-2020 OpenCompany, LLC