Skip to content

Engineering

Development Team Rules

  • Process Etiquette:

  • Keep commit messages clean and readable.

  • Keep branch name and bugs readable.
  • Keep gitlab branches clean (remove old branches)
  • No MR without a Story or a bug.
  • MR must contain the link to Gitlab issue
  • Any issue that consists of both Frontend and Backend changes, an MR should only be made when both changes are completed.
  • 1 Branch per Story.
  • Only work on one story at a time
  • An issue is owned by only 1 engineer, but an issue can have multiple collaborators
  • An engineer who is the owner of an issue must ensure the issue functions properly before merging to master.
  • Do not keep a Story for more that 2 days in Build/Merge/Staging/verify without team consensus.
  • Create a MR only when you are ready to merge. Avoid 'Draft' and 'WIP'
    • Attach a gif of how your code works if it involves visual changes. You can download LICEcap app for this, it's free.
    • Reach out to another engineer to help verify your feature in staging before marking it as "Staging Verified"
    • Add 2 reviewers when creating the MR.
  • If a bug is identified on staging for a story, move that ticket back to todo and do not create a new ticket for the bug
  • Code Etiquette:

  • Always leave code cleaner than when you first found it. Refactoring is good.

  • Blockers - Do not spend more than 10 mins.
  • ABT (Always Be Testing)
    • Test coverage on new feature must be 90% or above.
    • Test coverage on existing feature must never decrease.
  • Follow Rails resource routing convention when designing React pages or modals. E.g /labels, /labels/:id, /labels/new, etc.
  • Do not delete records, create a status field and flag them as 'deleted' instead, then create a default model scope to always return undeleted records.

Responsibility of an Engineer

Pick up an issue and you own it. You will be responsible for :

  1. Writing the feature

  2. Ensuring the code gets reviewed properly. GitLab is setup to have 2 approval before merging

  3. Merging the code to master - once the MR is merged, the code is deployed on staging

  4. Testing proper function in staging

  5. Getting Sign-off from another Engineer.

  6. Pushing to production - and of course - ensuring the feature functions properly in production.

Sprints

Emergency Response Sprint

On occasion, a situation dictates that engineering iterates faster than the regular sprint cycle. At DoubleDGP, we call that an Emergency Response Sprint. Epics labeled 'Emergency Escalation' have stories which are called 'Emergency Escalation' stories. If a ticket is in the TODO column and does not have an Epic labeled 'Emergency Escalation', DO NOT work on it until ALL the 'Emergency Escalation' stories are completed. New 'Emergency Escalation' stories maybe added to the TODO column every day and may contains short videos describing bugs in the application. Each videos may contain multiple bugs and all bugs must be fixed in the same ticket. Engineers must use follow the 'priority' labels when deciding which 'Emergency Escalation' stories to work on next.

Engineering Sprint Board Flow

Start working on a story you are assigned to from the Todo Column. If you do not have a story assigned to you from the Todo column, pick the First story in the Open column.

  1. Review the story and make sure: - ​ You understand it. (If not, contact the product manager responsible and ask for clarification) - ​ The story follows the DoubleDGP User story format. (If not, contact the product manager responsible because the story should be revised). - In case the Story is missing designs, move the story to the UI/UX DESIGN.
  2. Move the Story to the Doing Column and assign it to yourself to indicate you are working on it.
    • Set the Due Date
    • if the ticket is taken in the morning add (weight -1 ) days to the current date
    • if the ticket is taken in the afternoon, add (weight ) days to the current date
  3. Create a branch from master using the brief description of the user story as the branch name.
  4. When completed, create a merge request to merge to master - Find 2 other engineers that can get review your code.
  5. You will get a notification when your story is code review and is either accepted and merged, or is rejected and some issues must be corrected.
  6. If the merge is successful, the code reviewer moves the story to the Staging column for the you to validate the story functions correctly in the staging environment.
  7. Once you verify the story in the Staging column functions according to the acceptance criteria, you must move the ticket to Closed column

UI/UX Sprint Board Flow

The UI/UX Sprint Board uses the 2 columns (UX:Design and UX:Review) on the Engineering Sprint Board.
Designers must start working on a story from the UX:Desing Column. If you do not have a story assigned to you from the UX:Desing column, ask your supervisor for direction.

  1. Review the story and make sure: - ​ You understand it. (If not, contact the product manager responsible and ask for clarification) - ​ The story follows the DoubleDGP User story format. (If not, contact the product manager responsible because the story should be revised). - In case the Story is missing designs, move the story to the UI/UX DESIGN.
  2. Move the Story to the Doing Column and assign it to yourself to indicate you are working on it.
    • Set the Due Date
    • if the ticket is taken in the morning add (weight -1 ) days to the current date
    • if the ticket is taken in the afternoon, add (weight ) days to the current date
  3. When completed, move the ticket to the UX:Review and let the stakeholder by tagging them in a comment.
  4. Once reviewed and approved, the ticket may be promoted to an epic depending on the scope and will be assigned a label of eng::planning (when promoting to the epic the current ticket will get closed. It should be reopened and added to the newly created epic and add the tag to the ticket eng::planning)

Anatomy of a user story

Kindly note that these fields are required for a story to be worked on

  1. Brief title*: short description (4 - 5 words) that best describes the story
  2. Description*: As a < some type of user >, I want < to do something > so that < some business reason that provides value is achieved>.
  3. Features: Enumerated list of key features to include
  4. Acceptance Criteria*: Usually a list of steps to follow to complete the definition done.
  5. Stakeholder*: The person who reported or suggested the issue

Anatomy of a bug

Kindly note that these fields are required for a bug to be worked on

  1. Summary: short description that best describes the bug
  2. Steps to reproduce - well explained steps on how the bug happens
  3. What is the expected correct behavior? - explain how the feature should normally work.
  4. Who should verify on Staging? - person responsible for this bug
  5. Relevant logs and/or screenshots - links and any other information that might help to quickly fix the bug
Engineering based issues

When improvements needs to be made to the code, As an Engineer you can should create a story for it with: 1. Brief title: short description (4 - 5 words) that best describes the story 1. Description: reason for the improvement 1. What's wrong with what we have 1. Solution: Explain the new suggested solution 1. Will it have an impact on end user ? Yes or No

When you are done, add a label called engineering to the story so that it is separated from product stories and discuss it with other engineers during Engineering Improvements meeting that happens bi-weekly on Mondays.

What to do when the Plan Column is empty but the sprint is not over.

If you do not have a story assigned to you from the Ready to Implement column, pick the last story in the Ready to Implement column.

Contributing to the codebase

Most contributions will be based on a the current sprint in gitlab(including bugs) and its user stories as indicated above, here are few things to keep in mind:

  • Make sure you have read the README
  • Contributions should be in form of merge requests
  • All changes new and updates should have accompanying tests
  • Merge Requests should include a brief description with a link to the ticket it fixes and a screen recording of how it works.
  • Merge requests should all include a template description found here

  • Make sure the following checklist is followed and checked

    • [ ] Merge Request does not include breaking changes
    • [ ] I have tested and verified security guard's flow works fine
    • [ ] I have tested and verified event logs works fine
    • [ ] Merge request does not contain merge conflicts
    • [ ] I have linked the source and name of any newly added gem or node_module package

If you are contributing to the react codebase, you will need to have at least node v12 installed locally to be able to run checks before you commit and push, you can find out how to install node on different platforms here

When writing test for react, we highly recommend you use react-testing-library because we are incrementally removing enzyme in our codebase to make our tests easy to write and giving us the ability to test what the user sees rather than the implementation.

For convenience, we recommend you add these aliases, you might need them quite often

if you are on a UNIX based OS, edit ~/.bashrc file and add these lines

alias rlint='docker-compose run --rm rails rake lint:fix'
alias rtest='docker-compose run --rm rails rake'
alias ytest='docker-compose run --rm webpacker yarn run test'
alias ylint='docker-compose run --rm webpacker yarn run lint'

You can customize aliases according to your liking, then when you want to run backend test, you can just type rtest

We encourage to keep the codebase cleaner than you found it, refer to the following standards that we use in our codebase to get familiar with them.

PropTypes are to defined on every components when contributing to the react codebase.

Storybook

As Engineer, you should strive to always create components that can be reused in the application for similar purposes that it serves, this way we reduce the amount of work needed to be done and it also helps us be consistent in how we do things.
Every reusable component should be documented in storybook, You can find its documentation here http://storybook.js.org.

Example of how a simple story is written

Say you have Button in components/Button.js

export default Button({isDisabled}){
  return <button disabled={isDisabled}></button>
}

Button.propTypes = {
  /**
   * This determines whether the button is clickable or disabled
  */
  isDisabled: PropTypes.bool.isRequired,
}
In the Stories directory you create a file with the name of your component.stories.js in our example it would be button.stories.js

// import the file in stories directory from the components directory
import Button  from '../app/javascript/src/components/Button'

export default {
   // Add the story to components category or a matching cateogory depending on what it is.
    title: 'Components/Button',
    component: Button,
}
// If the component has arguments which is likely to be the case
// then define different versions of that component with the props

const ButtonTemplate = (args) => <Button {...args} />;

export const SimpleActiveButton = ButtonTemplate.bind({});

SimpleActiveButton.args = {
  isDisabled: true',
};

export const DeactivatedButton = ButtonTemplate.bind({});

DeactivatedButton.args = {
  isDisabled: false',
};

After saving your changes, navigate to http:localhost:6006 and you should be able to see the newly created component reflect in storybook

Engineers must read the Storybook tutorial: https://www.learnstorybook.com/intro-to-storybook

The DoubleGDP Storybook documentation can be found here: https://doublegdp.gitlab.io/app/

End to End Testing

We use Cypress for our end-to-end testing due to the many benefits it provides and its excellent documentation. As of this writing, we have 3 Cypress test scenarios (Gate Access, Timesheet, and Payment) and we hope to add more as we progress. This integration is included in our Gitlab CI pipelines and the tests are run when deploying to Staging, this gives us the confidence to go ahead with the production push in case of no Cypress failure.

How To Add a New Cypress Test
  • Create a new file inside the cypress/integration directory. E.g cypress/integration/timesheet.js
  • Write your code. We can group the code into four parts as shown in the example below:
describe('Time Sheet', () => {
  it('allows custodian to record time shift', () => {
    // First part
    cy.factory('community', { name: 'Nkwashi' }).then((communityResponse) => {
      cy.factory('store_custodian', {
        name: 'Mr Custodian',
        phone_number: '2348167740149',
        email: '[email protected]',
        state: 'valid',
        community_id: communityResponse.body.id
      })
      cy.factory('security_guard', {
        name: 'A Guard',
        phone_number: '2347065834175',
        email: '[email protected]',
        community_id: communityResponse.body.id,
      })
    })

    // Second part
    cy.login('2348167740149')

    // Third part
    cy.visit('/search')
    cy.get('.user-search-input').type('A Guard').type("{enter}")
    cy.wait(2000)
    cy.get('.user-search-result').click()
    cy.wait(1000)
    cy.get('#closeBtn').click()
    cy.get('.start-shift-btn').click()
    cy.wait(20000)
    cy.get('.end-shift-btn').click()

    // Fourth part
    cy.visit('/timesheet')
    cy.get('.shift-user-name').should('contain', 'A Guard')
  })
})

The first part is the factory part that allows you to seed the test DB with the necessary data you are going to be needing. In this example, we created three records; community and two user records (a custodian and a security guard). Note that the first argument supplied to cy.factory() must be a valid factory name, i.e store_custodian and security_guard can be found in spec/factories/user/user.rb and community can be found in spec/factories/community.rb.

The way cy.factory() command works is that it makes a request to the factories_controller using the second argument as the body params. As a result of this, we should always make sure we require the necessary factory files at the top of the controller file. Below is what it currently looks like:

unless Rails.env.production?
  require Rails.root.join('spec/factories/community.rb')
  require Rails.root.join('spec/factories/users/user.rb')
  require Rails.root.join('spec/factories/properties/account.rb')
  require Rails.root.join('spec/factories/properties/land_parcel_account.rb')
  require Rails.root.join('spec/factories/properties/land_parcel.rb')
end

At the same time, we should ensure we add new body params as part of the permitted attributes in the factory_attributes method of the same controller. Here's what it currently looks like:

  def factory_attributes
    params.fetch(:attributes).permit(
      :name,
      :phone_number,
      :email,
      :state,
      :community_id,
      :parcel_number,
      :user_id,
      :land_parcel_id,
      :account_id,
    ).to_h
  end

The second part is the login part, you basically pass in the phone-number of the user you want to log in to cy.login(). Note that the phone-number must be that of an already created user.

The third part is where you mimic user interaction on the app, such as visiting a URL, clicking a button, typing into an input field, etc. You can check the documentation here for a better understanding and of this part.

The fourth part is where you do assertions based on the third part you've written, such as; verifying that a div contains a certain value, a button is enabled/disabled, etc.

How To Run Cypress

Run sh ./bin/integration_tests.sh Note that this takes some time to run as compilation usually takes a few minutes.

If you rebuild before running the tests, probably because you have some new gems, you should pass an argument to the command. E.g sh ./bin/integration_tests.sh 'ci'. This will build and then run the tests.

How To Debug Cypress Failure

Cypress errors are usually self-explanatory. In case of confusing error messages, you can check recorded videos to see what's going on. Every time you run Cypress tests, it automatically records a video for each test file and saves them in tmp/cypress/videos, you can go in there, download the file and watch.

Code review

Merge request must be reviewed by 2 or more people. The engineer(s) who did the work and the reviewer. If the changes are simple enough the reviewer can approve and merge the changes without having the engineer who produced the code present.

it is also encouraged for engineers to review each other's code once they submit a merge request and approve it, if it is good and ready to be merged.

Staging Verified

As a engineer, you MUST complete the 'Engineer Checklist' for the acceptance Criteria - Requirements. You must demonstrate to another engineer you completed all the acceptance criteria. A loom video link must be attached to the MR showing the engineer going through all the acceptance Criteria of the ticket to the reviewing developer. The reviewing developer can ask questions, clarifications, and additional tests to be conducted. If the checklist is completed, add this label Staging::Verified. If not, leave a comment in the issue saying why it is not working and add this label Staging::Bug and fix this issue. You can use initial branch to make the change, since branches get automatically deleted to add required fix and create an MR. If you closed the issue then reopen.

Deploying to Production

All issues must have Stakeholder signoff before deploying to production

You may be deploying to production not only your own issue, but other engineers issues as well. When this case arises, please notify them that you will be deploying shortly and they need to be ready to jump to support their issues. Make sure all the stories in staging have a label of Staging::Verified

Before deploying to production there are a 2 tests scenarios 'Gate Access Test' and 'Time Sheet Check in/out Test' that must be done. Login credentials are in 1Password in the Developer Vault.

Gate Access Test

If you don't use a mobile phone:

  • Login as a guard in incognito mode using: [email protected]
  • Find yourself as a admin
  • on the profile page of the selected user, in the url add this /:tm/:dg e.g: /1618332435960/dg or /1618332435960
    • You can use this to get the timestamp for today Date.now()
    • The full url should match this /user/:id/:tm/:dg
  • select log this entry.
  • Use your admin account from you regular browser to check the logs to make sure the log entry was recorded successfully.

if you have a mobile phone you can:

  • Login as a guard in incognito mode using: [email protected]
  • From your mobile device:
  • login as an admin on your mobile device.
  • Select the ID tile to display the QR code.
  • Scan the ID code using the guard browser.
  • select log this entry.
  • Use your admin account from you regular browser to check the logs to make sure the log entry was recorded successfully.
Time Sheet Check in/out Test
  • Login as a custodian in incognito mode using: [email protected]
  • Find the guard 'mama Guard'.
  • Start timer, wait 20 sec, then Stop Timer.
  • Verify the entry is present in the Timesheet log.
Payment Test
  • Login as an admin on staging-demo
  • Find a client user with a payment plan and take note of the current balance
  • Make a payment and note the balance
  • Check the receipt
  • Revert the payment and note the balance
Deployment

When the tests are successful, you will deploy by simply creating a new tag with the following REQUIRED information:

  • Tag Name: Find the latest tag that was released and increment by 1. (if latest version is 0.0.37 then the new tag name is 0.0.38)

  • Message: The list of issues listed in a non markdown format.

  • Release Notes: The list of issues listed in a markdown table format - see below. (should include IssueID, URLlink to the issue in gitlab., Title of the issue.)

  • Issue ID URL Title
    123 https://gitlab.com/doublegdp/app/-/issues/123 ? and special characters showing in SMS campaigns from system
    185 https://gitlab.com/doublegdp/app/-/issues/185 Ability to delete unsent campaigns for admins

Issues

Issue priority levels

Issues are triaged and prioritized in terms of severity. There are several priorities of issues:

  • P0: issues that have impacted all of production and are preventing a large segment of users from accessing the application. These are "all hands on deck" situations.
  • P1: issues that are severe enough that they have to be fixed within the current sprint. A story may have to removed from the sprint if the P1 issue takes more than 4 hours to resolve.
  • P2: issues that can be addressed in the following sprint or in a later sprint. P2 issues are prioritized by the product manager and are treated the same as stories during Sprint Planning.
  • All others: tasks included in the Dev Sprint project are stack ranked, which gives an explicit prioritization. Those in other projects without a tag are not yet prioritized.

Reporting an Issue

Reporting of an Issue

Use Gitlab here

Submit issue in the correct format of an issue (See below).

PS: You must have a gitlab account

Monitoring Issues In Production

We use Rollbar to alert us of javascript and rails exception that have occurred either on the server or client side

Format of an issue that's tagged as 'bug'

  1. Brief description: What it will show when compressed.

  2. What it does: Describe what it currently does.

  3. What it should do: Describe what you believe it should do.

  4. How to reproduce: Describe the steps to reproduce the Issue. If the issue is an exception from Rollbar, add the Rollbar link reference.

  5. Assignee: Assign the Product Manager if it's a high priority, so it's seen immediately. Otherwise, the Proudct Manager review the list of new bugs/issues once a week.

RCCAs (Root Cause and Corrective Action)

Root Cause and Corrective Actions are essential for documenting what went wrong in Production and proactively assuring business continuity. They are logs containing production issue events, their resolutions, and potential follow up action items.

Post Mortems 2 15- minutes meetings:

  • Initial 15 mins meeting to fill in the information according to the 'anatomy of a RCCA'.
  • Post Mortem resolutions should always result in a Merge Request.
    • Whether a code MR is created
    • And/or a Procedural change - a hand book MR.
  • Follow up meeting to document the steps actually taken to prevent or mitigate the issue from happening in the future.

Root Cause and Corrective Actions are accessible by anyone at DoubleGDP and can be communicated outside the company.

Anatomy of an RCCA

  • Description: Brief synapsis of what happened.

  • Who was affected

  • Fix(immediate): What was done to fix the issue.

  • Long Term Fix: if there is a need for the issue to be addressed. long term, then:

  • Create a List of stories.
  • Create a list of Procedure changes.

  • Setup Follow up meeting date.

  • Use the template named 'rcca'

  • Follow up meeting:

  • GitLab Ticket will be created to track what steps were actually taken to mitigate the issue.

#### Support Coverage

When engineering members take time off for more than a day, engineering member must ensure their duties are transfered to another member to ensure business continuation.
transfer the responsibility if: - You are in charge of leading team meetings. - You are usually 'on call' schedule

How to tranfer
  • Create a ticket in the handbook to handover the responsibility and assign it to you and the individual you are transfering the responsibility to.
  • The ticket should include a hand off meeting and description of responsibilities

Resources Available

Resource Description
Dev Ops Playbook Deployment and debugging processes
On Call Schedule WIP through PagerDuty. Default is: Olivier during CAT working hours, Mark during ET working hours, Nolan all other times.
App Sourcecode GitLab repo, publicly available. It has a README.
Anonymized Customer Database DB based on Nkwashi customers, but with anonymized names, fake phone numbers and NRCs, and scrambled plots.