Mattermost Engineering Knowledge Base
Engineering knowledge base maintained by Tommy ⚙️ for Pavel Zeman’s engineering management at Mattermost.
Recent Updates
PR Status Tracker
Trackers · Last updated Oct 30, 2026Running status of all active PRs across Mattermost repos. Last updated: 2026-10-23 17:10 UTC
Sentry Fix PRs
| # | Sentry ID | Repo | Title | PR | Status | Events | Users |
|---|---|---|---|---|---|---|---|
| 1 | SERVER-VF | mattermost | Nil pointer in UpdateUser | #35717 | ✅ Merged | 14 | TBD |
| 2 | SERVER-W9 | gosaml2 | SAML decrypt panic + expired test cert | #9 | ✅ Merged | 11 | 1 |
| 3 | MOBILE-IOS-YND+YNE | mobile | Fatal SQLite crashes from concurrent DB access | #9610 | ✅ Merged | 46,000+ | 36,000+ |
| 4 | MOBILE-ANDROID-AX68 | mobile | RNN race condition crash during activity destroy | #9617 | ✅ Merged | 9,150 | 1,580 |
| 5 | MOBILE-ANDROID-AZ02+AYPW | rn-network-client | OkHttp header crashes from non-ASCII chars | #160 | ✅ Merged | 1,395 | 654 |
| 6 | SERVER-W1 | mattermost | createProfileImage panic on empty username | #35722 | ⚠️ CR (Draft) | 1,793 | 167 |
| 7 | SERVER-TY | mattermost | Nil License().IsCloud() sweep — 33 call sites | #35718 | 🟡 Draft ❌ | 1,497 | 1 |
| 8 | MOBILE-ANDROID-AWY6 | mobile | Push notification crash on non-Map error data | #9618 | ✅ Merged | 4,806 | 574 |
Total user impact addressed: ~39,000+ users across 8 PRs.
Paid Search Attribution: Findings and Best Practices
Marketing & Attribution · Last updated Oct 30, 2026Source data: UTM_Raw_Data_Sept_1_to_Oct_15.csv (Marketo export, 5,125 unique people created 2026-09-01 to 2026-10-15) and how_lead_source_is_set_in_marketo.docx/.html (Person Source rules, audit of Oct 15, 2026).
1. Scope
List imports (3,632 rows, 71% of the export) can be ignored for web campaign attribution. None of them has any utm_*, ft_*, gclid or FT Referrer value, and Most Recent Lead Source is blank on all of them. Their Person Source was set by hand (3,468 “Unknown”, 150 “Live Event”, 12 “Paid Social”, 2 other). They are mostly ABM and event lists.
From 62 Minutes to 14: How We Sharded Our Go Test Suite
Blog Drafts · Last updated Oct 30, 2026At Mattermost, our server test suite had a problem: it took about 62 minutes to run on every pull request. For master builds with the race detector enabled, that number climbed to over 90 minutes. We recently shipped test-level sharding (#35739) that brings PR runs down to ~14 minutes — a 77% improvement.
Thanks to Jesse (lieut-data), Doug (wiggin77), and Eva (esarafianou) for a thorough code review that caught real issues and made the implementation significantly better.
Server Test Coverage Strategy
CI & Coverage · Last updated Oct 30, 2026Date: 2026-03-23
Baseline: 33.84% (201,671 lines across 853 files)
Source: Codecov commit report
Current State
| Metric | Value |
|---|---|
| Production Go files | 1,287 |
| Files with coverage data | 853 |
| Total statements instrumented | 146,513 |
| Statements covered | 53,132 (36.3%) |
| Statements uncovered | 93,381 |
The 33.84% figure is real production coverage — generated code (retrylayer, timerlayer), mocks, and test utilities are correctly excluded from the denominator. The gap between 1,287 total files and 853 instrumented files is mostly generated layers and mock code that Go’s coverage tool correctly ignores.
Website Forms and Tracking Audit
Marketing & Attribution · Last updated Oct 30, 2026Repo audited: /Users/pavelzeman/projects/mattermost/mattermost-website (Astro, deployed through Cloudflare Workers; commit 4aeda543).
Severity: High = loses or corrupts attribution today. Medium = inconsistent or a risk. Low = hygiene.
1. Summary
All lead capture goes through one adapter, src/components/utils/marketo-forms.ts (1,087 lines).
That is good news for the rebuild, because there is one place to change. But the adapter is entirely Marketo-specific, so it has to be replaced for Pardot, not just reconfigured.
Workstream Tracker
Trackers · Last updated Oct 30, 2026Last updated: 2026-03-20
1. Sentry Issue Triage & Fix
Goal: Identify, fix, and PR low-hanging-fruit Sentry issues across Mattermost projects.
Full log: Sentry Fixes Log
Summary (as of 2026-03-20)
- 11 issues triaged across server, mobile (iOS + Android), and supporting libraries
- 8 draft PRs created with tests
- ~39,000+ users impacted across all fixes
- 1 upgrade advisory published (QC cluster panic, fixed in v11.0.1+)
- 1 skipped (single modified fork)
- 1 in progress (VF — nil pointer in patchUser/UpdateUser)
Biggest wins by user impact
| Issue | Platform | Users | PR |
|---|---|---|---|
| YND+YNE — SQLite concurrent access | iOS | 36,000+ | #9610 |
| AX68 — RNN activity destroy race | Android | 1,580 | #9611 |
| AZ02+AYPW — OkHttp header crashes | Android | 654 | #155 |
| AWY6 — Push notification type cast | Android | 574 | #9609 |
| W1 — Empty username panic | Server | 167 instances | #35700 |
Next Steps
- Complete VF investigation (nil pointer in patchUser/UpdateUser, 14 events)
- Get reviewer assignments on green PRs
- Pavel: build/test #9610 (iOS SQLite) on M4 MacBook Air
2. CI Improvements
Goal: Faster CI pipelines for mattermost/mattermost server.
Marketo and Salesforce Configuration Issues
Marketing & Attribution · Last updated Oct 30, 2026Issues found in how UTM and attribution data is captured and stored across Marketo and Salesforce, based on the campaign-by-year analysis in sfdc-utm-campaigns-over-the-years.md.
Provenance: The findings below came from a separate analysis that was pasted in. I did not run the Salesforce queries and have not verified the claims. Items that are diagnoses or guesses, not observed facts, are marked Hypothesis. Where the Marketo CSV I analyzed supports or contradicts a claim, that is noted.
CI Coverage Persistence — Current State & Options
CI & Coverage · Last updated Oct 30, 2026Date: 2026-03-23 (updated)
Context: PR #35743 re-enabled server test coverage with 4-shard parallelism. Coverage uploads to Codecov succeed, but the developer experience is incomplete.
Where We Are
What works:
- 4-shard coverage run completes without OOM (6-8 min per shard)
- Merged
cover.outuploads to Codecov successfully (3.5MB, 853 files) - Codecov commit report exists: link
What doesn’t work:
- No PR comments from Codecov showing coverage diff
- No
codecov.yml— everything runs on Codecov defaults - Generated code inflates the denominator (see below)
- No
cover.outartifact saved for historical analysis - No baseline on
masteryet — Codecov can’t compute diffs without a reference point
The Numbers Problem
Go’s coverage tool instruments everything compiled into the test binary, including generated wrappers, mocks, and test infrastructure. This inflates the denominator:
CI Pipeline Analysis v2: mattermost/mattermost
CI & Coverage · Last updated Oct 30, 2026Last updated: 2026-04-09 — based on master run 24166497256. Follow-up to v1 analysis from 2026-03-20.
Progress Summary
Three weeks of CI improvements have reduced the PR critical path from ~60min to ~11min — a 5.5x speedup.
| Metric | v1 (2026-03-20) | v2 (2026-04-09) | Change |
|---|---|---|---|
| PR critical path (wall-clock) | ~60min | ~11min | -82% |
| Master critical path | ~93min (with -race) | ~11min (no race) | -88% |
| Postgres (PR, longest shard) | ~60min (1 runner) | ~8m 42s (4 shards) | -85% |
| Postgres FIPS (PR, longest shard) | ~60min (1 runner) | ~9m 01s (4 shards) | -85% |
| mmctl | ~8min | ~10min | unchanged |
| Build | ~10min | ~10m 40s | unchanged |
| Binary parameters (master) | ~87min | 15m 47s ❌ (killed) | broken |
| Race detection (master) | every push (~93min) | effectively disabled | ⚠️ |
| Test coverage | disabled (OOM) | in progress (PR #35743) | 🔄 |
What Shipped
| Phase | PR | Status | Impact |
|---|---|---|---|
| Phase 4: Server test sharding | #35739 | ✅ Merged | 4 parallel shards per test matrix |
| Fullyparallel mode | #35816 | ✅ Merged | t.Parallel() within each shard |
| Phase 5: Test coverage | #35743 | 🔄 In progress | 4 coverage shards, codecov.yml consolidated |
| Phase 6: Race detector | — | ⚠️ Changed | Effectively disabled; nightly workflow needed |
New Issues Discovered
- Binary parameters mode hangs — running with fullyparallel causes runner kills (PR #35992 investigating)
- Race detector effectively disabled — condition
if [[ github.ref_name == 'master' && fullyparallel != true ]]never fires since fullyparallel defaults to true - Build is now the bottleneck — at ~11min, it’s the longest job on the critical path (previously dwarfed by 60min tests)
1. Workflow Inventory
No changes to the workflow inventory since v1. The repository still contains 31 workflow YAML files. See v1 analysis for the full list.
PR Daily Status
Trackers · Last updated Oct 30, 2026Running daily log of PR status updates. Newest entries at top.
2026-10-30 (Friday)
27 open PRs. Since 2026-10-29: no merges, no new PRs, no new unresolved threads, no new reviews. Only change: mattermost#35722 (draft) is now MERGEABLE/BLOCKED (was reported unknown yesterday).
🟢 Near-Ready (approved, CI green, MERGEABLE):
- mattermost#36181 — Strip debug symbols — APPROVED ✅, CI green (61 checks, 0 failing), MERGEABLE/CLEAN. ⚠️ 2 unresolved threads (JulienTant, gate stripping on
MM_DEBUG).
🟡 Awaiting Review (CI green):
What Not to Do: Writing Go Tests That Survive Parallel Execution
Blog Drafts · Last updated Oct 30, 2026When we set out to re-enable test coverage with sharding on our server CI, we hit a wall within minutes: 45 tests crashed — not failed, crashed — taking down the entire test binary for the package. The culprit was a single line of code that’s perfectly fine in serial tests but lethal under parallel execution.
This post catalogues what we learned about writing Go tests that work in parallel mode, drawn from debugging real failures across our 16,000-test codebase.
Salesforce UTM Campaigns Over the Years
Marketing & Attribution · Last updated Oct 30, 2026Lead counts by UTM campaign value, grouped by year, from Salesforce. 23,809 leads across 11 years (2016 to 2026).
Provenance: These figures were pasted in from a separate analysis. I did not run the query and have not verified the numbers. The Salesforce field used for the campaign value, the field used to assign the year (likely created date), and the query date are not stated. Only the per-year lead totals were checked: they sum to 23,809.
CI Pipeline Analysis: mattermost/mattermost
CI & Coverage · Last updated Oct 30, 2026Last updated: 2026-03-20 — based on actual YAML files from the mattermost/mattermost repository and real timing data from GitHub Actions API.
1. Workflow Inventory
The repository contains 31 workflow YAML files in .github/workflows/. Here is a complete inventory:
| # | File | Name | Triggers | Purpose |
|---|---|---|---|---|
| 1 | api.yml | API | push(master), pull_request | Builds OpenAPI spec in api/ dir |
| 2 | build-opensearch-image.yml | Opensearch Docker Image | push(master) when Dockerfile changes | Builds/pushes opensearch 2.7.0 image |
| 3 | build-server-image.yml | BuildEnv Docker Image | push(master), PR, workflow_dispatch when Dockerfiles change | Builds/tests/pushes build-server and build-server-fips Docker images |
| 4 | claude.yml | Claude Code | issue_comment, PR review comment, issues, PR review | AI assistant for @claude mentions |
| 5 | codeql-analysis.yml | CodeQL | PR(master), schedule(30 5,17 * * *) | Security scanning for Go and JavaScript |
| 6 | docker-push-mirrored.yml | Push mirrored docker images | push(master) when mirror scripts change | Mirrors Docker images |
| 7 | docs-impact-review.yml | Docs Impact Review | pull_request | AI-powered docs impact analysis via Claude |
| 8 | e2e-fulltests-ci.yml | E2E Tests | workflow_dispatch | Full E2E test orchestrator (Cypress + Playwright), triggered by Argo Events |
| 9 | e2e-tests-check.yml | E2E Tests Check | PR when e2e-tests/, webapp/platform/client/, etc. change | Lint/typecheck E2E tests, trigger E2E runs for E2E-only PRs |
| 10 | e2e-tests-ci-template.yml | E2E Tests Template | workflow_call | Legacy reusable E2E template (used by e2e-fulltests-ci.yml) |
| 11 | e2e-tests-ci.yml | E2E Tests (pull request) | workflow_dispatch | PR E2E test orchestrator — resolves PR, checks file changes, calls Cypress + Playwright |
| 12 | e2e-tests-cypress-template.yml | E2E Tests - Cypress Template | workflow_call | Reusable Cypress E2E template with retries, reporting, sharding |
| 13 | e2e-tests-cypress.yml | E2E Tests - Cypress | workflow_call | Cypress E2E entry point — runs “full” tests with 40 workers |
| 14 | e2e-tests-on-merge.yml | E2E Tests (master/release - merge) | workflow_dispatch | Post-merge E2E: enterprise + FIPS editions, Cypress + Playwright |
| 15 | e2e-tests-on-release.yml | E2E Tests (release cut) | workflow_dispatch | Release E2E: enterprise + FIPS editions from mattermost/ registry |
| 16 | e2e-tests-override-status.yml | E2E Tests - Override Status | workflow_dispatch | Manual override of failed E2E status checks |
| 17 | e2e-tests-playwright-template.yml | E2E Tests - Playwright Template | workflow_call | Reusable Playwright template with sharding, S3 upload, retries |
| 18 | e2e-tests-playwright.yml | E2E Tests - Playwright | workflow_call | Playwright E2E entry point — runs “full” tests with 4 shards |
| 19 | e2e-tests-verified-label.yml | E2E Tests/verified | PR labeled | Override failed E2E checks via E2E Tests/verified label |
| 20 | i18n-ci-pr.yml | i18n CI PR | PR when i18n files change | Checks only English translation files changed (non-weblate PRs) |
| 21 | i18n-ci-template.yml | i18n CI Template | workflow_call | Template for i18n checks |
| 22 | mmctl-test-template.yml | mmctl CI | workflow_call | Reusable mmctl test template (docker-based) |
| 23 | scorecards-analysis.yml | Scorecards | branch_protection_rule, schedule(44 6 * * *) | OSSF Scorecard supply-chain security |
| 24 | sentry.yaml | Sentry Upload | workflow_run(Server CI completed) | Creates Sentry release on Server CI completion |
| 25 | server-ci-artifacts.yml | Server CI Artifacts | workflow_run(Server CI completed) | Uploads artifacts to S3, builds Docker image, Wiz security scan |
| 26 | server-ci-report.yml | Server CI Report | workflow_run(Server CI completed) | Publishes JUnit test reports, comments flaky tests on PRs |
| 27 | server-ci.yml | Server CI | push(master, release-*), PR(server/)** | Main server CI: lint, tests, build — THE critical workflow |
| 28 | server-test-template.yml | Server Test Template | workflow_call | Reusable server Go test template |
| 29 | tag-public-module.yaml | Tag Public Module | workflow_dispatch | Tags server/public module with semver |
| 30 | webapp-ci.yml | Web App CI | push(master, release-*), PR(webapp/**) | Webapp lint, typecheck, tests (sharded), build, coverage |
2. Job Catalog
2.1 Server CI (server-ci.yml) — The Critical Path
Triggers: push to master/release-*, pull_request on server/** paths.
Code Review Checklist
Processes · Last updated Oct 30, 2026Before Writing Code
-
grep -rnthe codebase for the pattern you’re fixing — fix ALL occurrences, not just the one you found - Check real API contracts for every input: does it throw, return null, coerce, or silently succeed?
- Understand the type system: identify all enum variants and type union members before writing logic
Production Code
- Use exhaustive
when/switchon enums and type unions — noelsecatch-alls that assume a type - Every error return/throw gets a unique detail string — never reuse the same message for different failure modes
- Defensive nil/null checks at trust boundaries
- No silent swallowing of unexpected cases
Tests
- Test the actual function, not a copy of its logic
- If a test helper mirrors production code (e.g., coroutine callback), document it and note it must stay in sync
- Mocks match real behavior: verify what the real API does for each input before mocking
- Cover all type/enum variants, not just the happy path
CI / Shell Scripts
- Run the full script locally under
set -euo pipefailbefore pushing — no experimenting in CI - Initialize all arrays with
=()to avoid “unbound variable” underset -u - Test with empty inputs and empty arrays
- Escape shell metacharacters in variables
- YAML-parse workflow files — don’t hand-edit complex YAML blindly
PR Submission
- Sentry impact summary at the top of the PR description
- Include a
release-note:block where required - No fork migration mentions — employees push to org branches
- No internal/private URLs in public-facing text
Proposed Plan: Rebooting Advertising, Lead Capture and Attribution
Marketing & Attribution · Last updated Oct 30, 2026Status: Proposal for review. Draft dated 2026-10-15. Scope: Google Ads and Microsoft (Bing) Ads, the mattermost.com website and landing pages, Marketo (migrating to Pardot by December 2026), Salesforce, and the reporting layer. Premise: We are willing to discard the current setup and rebuild it. Historical data is archived, not migrated as-is.
Pardot is now sold as Marketing Cloud Account Engagement. This document says “Pardot” throughout, as the team does.
1. Why we are doing this
Evidence from the three analyses in this folder (best-practices.md, sfdc-utm-campaigns-over-the-years.md, marketo-sfdc-configuration-issues.md). The Marketo export figures cover Sept 1 to Oct 15, 2026. The Salesforce findings come from a pasted analysis I could not verify.