diff --git a/blog/2026-05-29-developer-tenets.mdx b/blog/2026-05-29-developer-tenets.mdx new file mode 100644 index 000000000..f9ab774c7 --- /dev/null +++ b/blog/2026-05-29-developer-tenets.mdx @@ -0,0 +1,57 @@ +--- +title: Developer Tenets +authors: [eric, amy] +--- + +Open data projects like Overture are built across time. Contributors come and go, member companies rotate people in and out, and the builders of today are extending the work of those who came before them. Part of the work of Overture is documenting _how_ we work, to help new contributors quickly get up to speed and to capture the immense technical and organizational knowledge built up over the past few years. + +![collaboration](assets/omf-collaboration.jpeg) +*OG Overture developers from multiple organizations taking a break from coding for some analog collaboration and documentation, February 2024.* + +Our contributor base itself is wide. People bring code, documentation, data analysis, quality checks, and more to the project, and they come from an incredibly diverse range of organizations: big tech, scrappy startups, nonprofits, government agencies, and open data communities. This diversity is a strength, giving us a wide range of perspectives and skills. It can also create friction, since these organizations bring different goals, tools, constraints, development velocities, and cultures. + +Rather than subjecting our contributors to a strict set of engineering guidelines, we want to communicate a set of higher-level values. These values should be reflected throughout the development cycle, in the documentation we write and the pull requests we post. Beyond giving developers flexibility, this approach is AI-friendly: skills and agents can use this document as broad context for testing intent. + +The "Developer Tenets" below are adapted from our internal Overture operations guide. We're publishing them because they're the kind of thing that benefits from being out in the open, where contributors and downstream users can see how we think about building Overture, at least from the engineering side. The tenets were defined by the developers, and Claude helped expand them with examples and code review guidance, i.e. "what to check." That pattern (humans setting direction, AI in a supporting role) is central to how we operate. + + +{/* truncate */} + + +## Follow the Footsteps of Those Before You + +_**What it is:**_ Understand and follow how we work as developers within Overture — the architecture we use as scaffolding, how we use GitHub, how we document code, and how we structure pipelines. Before starting work, take time to understand the conventions already in place. Contributions that ignore established patterns create maintenance burden and make code harder to review and debug. + +## Documentation Beyond Code + +_**What it is:**_ Code alone is not documentation. Requirements, design documents, test plans, and inline comments for complex business logic are all important parts of a properly-documented change. Documentation is not something added at the end of a project — it should be built out and reviewed during the appropriate development phase. This is especially important for Overture given its collaborative nature and the likelihood of developer turnover. When the next contributor picks up your work, the documentation should tell them what you built, why you built it that way, and how to verify it is working correctly. + +_**What to check:**_ Significant new features or architectural changes submitted without a linked design doc; complex business logic without inline comments explaining the "why"; new DAGs without doc_md populated; PRs that introduce new pipeline behavior without a test plan; documentation that appears to have been added only as a merge prerequisite rather than developed alongside the code. + +## Invest in Stability + +_**What it is:**_ Our code needs to be predictable and deterministic. Given a fixed set of inputs, the output should be the same every time. Extra effort should be invested in automation, handling edge and corner cases, full traceability of data through the pipeline, and writing code that is easy to test and verify. Instability in pipeline behavior is one of the most expensive problems at release time — it creates thrashing, difficult debugging sessions, and erodes trust in the data. + +_**What to check:**_ Non-deterministic behavior where results vary across runs without any change in input data; missing handling for null values, empty datasets, or unexpected input shapes; manual steps that could be automated and therefore made repeatable; changes that introduce hard-to-trace side effects on shared state or downstream stages; insufficient test coverage for failure modes and edge cases. + +## Design for Operations + +_**What it is:**_ The release operations team is the customer of every pipeline component we write. Code must be designed with its operators in mind — clear, actionable error messages; logging at the appropriate level for debugging; simple and consistent pipeline behavior; and documentation of common failure modes. A pipeline that fails silently or with cryptic errors places a disproportionate burden on the operations team and puts releases at risk. + +_**What to check:**_ New DAGs or task groups with no structured logging on failure; error messages that do not distinguish between configuration errors, data errors, and infrastructure errors; pipeline stages that can fail ambiguously where the error looks the same regardless of root cause; new critical-path components without notes on expected behavior or known failure modes; changes to retry counts, timeouts, or short-circuit logic without documentation explaining the intent. + +## Our Product is Data; Software is How We Build It + +_**What it is:**_ Overture's deliverable is high-quality, schema-compliant geospatial data. Code is a means to that end. Thorough testing of a change involves not only unit tests and scalability tests, but also evaluating the impact on the data we output. A technically correct implementation that degrades data coverage, completeness, or schema compliance is not complete. Before a change is considered done, the developer should be able to articulate the expected effect on output data. + +_**What to check:**_ PRs that change pipeline logic without demonstrating impact on output data — feature counts, attribute completeness, schema compliance; schema-affecting changes without a before/after data comparison; matchers or adjudicators updated without benchmarking against known data samples; new pipeline stages merged without running against real data; missing or removed data validation checks. + +## Balance Cost and Performance + +_**What it is:**_ Overture has limited resources. Optimization and performance need to be balanced with cost. Developers should be aware of the compute and storage costs their code incurs and should not leave low-hanging fruit on the table. The most common offenders are over-allocated Spark clusters and inefficient algorithms or design patterns that iterate through data unnecessarily. + +_**What to check:**_ Spark jobs with executor counts or memory allocations significantly larger than the data volume warrants; algorithms that perform multiple full dataset scans where a single pass would suffice; iteration over large datasets in Python where a set-based or Spark operation would be appropriate; SQL or Spark transformations that don't take advantage of partitioning or predicate pushdown; patterns that reprocess data that could be cached or reused. + +--- + +Curious to learn more or get involved? Visit our About pages to [meet the staff](https://overturemaps.org/about/who-we-are/) and [member organizations](https://overturemaps.org/about/members/) behind Overture. Dive into [the docs](https://docs.overturemaps.org/) and contribute to our [public projects on GitHub](https://github.com/OvertureMaps), subscribe to [our community newsletter](https://share.hsforms.com/1T38OzgVmQPG7AQTdAyBizQ4tvhy?__hstc=180738424.dd8e59c2fa5c30bf706b680bba6d5255.1774625963672.1779998929067.1780002709543.40&__hssc=180738424.4.1780002709543&__hsfp=549b384a322d494ea6d03f5ea8ef0a17) for regular updates, or [become a member](https://overturemaps.org/become-a-member/) to help shape the future of open map data infrastructure. diff --git a/blog/assets/omf-collaboration.jpeg b/blog/assets/omf-collaboration.jpeg new file mode 100644 index 000000000..7ec72cfd1 Binary files /dev/null and b/blog/assets/omf-collaboration.jpeg differ diff --git a/blog/authors.yml b/blog/authors.yml index 877401807..633b0e79a 100644 --- a/blog/authors.yml +++ b/blog/authors.yml @@ -1,6 +1,6 @@ dana: name: Dana Bauer - title: Technical Product Manager, Overture Maps + title: Technical Product Manager, Overture email: dana@overturemaps.org jonah: @@ -8,3 +8,16 @@ jonah: title: Cartography Lead, Meta email: jonahadkins@meta.com +amy: + name: Amy Rose + title: CTO, Overture + email: amy@overturemaps.org + +eric: + name: Eric Godwin + title: Principal Engineer, Overture + email: eric@overturemaps.org + +staff: + name: Overture Staff + email: info@overturemaps.org \ No newline at end of file diff --git a/docusaurus.config.js b/docusaurus.config.js index 8cb3f4890..f12b2ce2e 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -41,7 +41,7 @@ const latestOvertureRelease = getLatestOvertureRelease(); /** @type {import('@docusaurus/types').Config} */ const config = { - title: 'Overture Maps Documentation', + title: 'Overture Documentation', tagline: '', favicon: 'img/favicon.png', @@ -165,9 +165,9 @@ const config = { blog: isSchemaPreview ? false : { - blogTitle: 'Overture Maps Engineering Blog', + blogTitle: 'Overture Engineering Blog', blogDescription: 'Building Overture Maps', - blogSidebarTitle: 'Posts from the Overture Maps engineering team', + blogSidebarTitle: 'Posts from the Overture engineering team', blogSidebarCount: 20, }, theme: { diff --git a/src/css/custom.css b/src/css/custom.css index 2a04f0a3d..4a9403ef9 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -7,21 +7,13 @@ /* You can override the default Infima variables here. */ /* Force dark code blocks in light mode (Night Owl) */ -[data-theme='light'] .prism-code { - background-color: #1e1e2e !important; - color: #cdd6f4 !important; -} - -[data-theme='light'] pre { +[data-theme='light'] .prism-code, +[data-theme='light'] pre, +[data-theme='light'] code { background-color: #1e1e2e !important; color: #cdd6f4 !important; } -[data-theme='light'] code { - background-color: #1e1e2e; - color: #cdd6f4; -} - /* Syntax token overrides for dark code blocks in light mode */ [data-theme='light'] .token.comment, [data-theme='light'] .token.prolog, @@ -86,18 +78,9 @@ } [data-theme='light'] { - --ifm-color-primary: #2c2e7f; - --ifm-color-primary-dark: #3a48b8; - --ifm-color-primary-darker: #3340a3; - --ifm-color-primary-darkest: #2d388f; - --ifm-color-primary-light: #5362d1; - --ifm-color-primary-lighter: #6673d6; - --ifm-color-primary-lightest: #7985db; --ifm-link-color: #4051cc; --ifm-link-hover-color: #4051cc; - --ifm-code-font-size: 90%; --ifm-font-family-base: Noto Sans, Montserrat, Segoe UI, Roboto, Ubuntu, Cantarell, sans-serif; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } [data-theme='dark'] { @@ -116,7 +99,7 @@ } [data-theme='light'] .DocSearch { - --docsearch-primary-color: #3a48b8; + --docsearch-primary-color: var(--ifm-color-primary-dark); --docsearch-text-color: #1a1a2e; --docsearch-muted-color: #495057; --docsearch-container-background: rgba(0, 0, 0, 0.6); @@ -178,14 +161,14 @@ padding: 0.35rem 1rem; border-radius: 8px; border: none; - background: #2c2e7f; + background: var(--om-primary-navy); color: #ffffff; box-shadow: 0 2px 8px rgba(44, 46, 127, 0.25); transition: all 0.2s ease; } .navbar .DocSearch-Button:hover { - background: #3a48b8; + background: var(--ifm-color-primary-dark); box-shadow: 0 4px 12px rgba(44, 46, 127, 0.35); transform: translateY(-1px); } @@ -209,13 +192,13 @@ } [data-theme='dark'] .navbar .DocSearch-Button { - background: #0ec1bd; + background: var(--om-primary-teal); color: #1b1b2f; box-shadow: 0 2px 8px rgba(14, 193, 189, 0.3); } [data-theme='dark'] .navbar .DocSearch-Button:hover { - background: #4edad8; + background: var(--om-secondary-cyan); color: #1b1b2f; box-shadow: 0 4px 12px rgba(14, 193, 189, 0.4); } @@ -223,7 +206,7 @@ /* Teal left accent on h2 headings in docs */ .theme-doc-markdown h2 { padding-left: 0.75rem; - border-left: 3px solid #4edad8; + border-left: 3px solid var(--om-secondary-cyan); } /* Landing page: remove empty title header and excess padding */ @@ -801,7 +784,7 @@ letter-spacing: 0.05em; color: var(--ifm-color-emphasis-600); text-align: left; - border-bottom: 2px solid #4edad8; + border-bottom: 2px solid var(--om-secondary-cyan); } .theme-doc-markdown > table td, @@ -839,16 +822,12 @@ } .quickstart-tabs .tabs__item--active { - border-bottom-color: #4edad8; + border-bottom-color: var(--om-secondary-cyan); color: var(--ifm-font-color-base); font-weight: 600; background: transparent; } -[data-theme='dark'] .quickstart-tabs .tabs__item--active { - border-bottom-color: #4edad8; -} - /* Blog post author bylines */ .avatar-list { @@ -890,4 +869,252 @@ font-size: 0.8rem; color: var(--ifm-color-emphasis-500); margin-top: 0.2rem; +} + +/* ════════════════════════════════════════════════════════════════ + Blog styles — match the Overture brand system used in docs + ════════════════════════════════════════════════════════════════ */ + +/* ── Blog post layout ── */ + +/* Give the blog the same H2 teal accent as docs */ +.theme-blog-post-page .markdown h2, +.blog-post-page .markdown h2 { + padding-left: 0.75rem; + border-left: 3px solid var(--om-secondary-cyan); + margin-top: 2.5rem; +} + +/* Tighten H1 / metadata spacing on post pages */ +.theme-blog-post-page .markdown > header h1, +.blog-post-page article header h1 { + font-size: clamp(2rem, 4vw, 2.75rem); + line-height: 1.15; + letter-spacing: -0.01em; + margin-bottom: 0.75rem; +} + +/* Post date / read time row */ +.theme-blog-post-page time, +.blog-post-page time { + color: var(--ifm-color-emphasis-600); + font-size: 0.875rem; +} + +/* ── Post header divider ── */ +.theme-blog-post-page article > header, +.blog-post-page article > header { + padding-bottom: 0.5rem; + margin-bottom: 2rem; + border-bottom: 1px solid var(--om-secondary-cyan); +} + +/* ── Author bylines: card-style ── */ +/* The avatar-list rules already exist in custom.css; these extend them + for the blog post header specifically */ +.theme-blog-post-page .avatar-list, +.blog-post-page .avatar-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1rem; + margin: 1.25rem 0 0rem; +} + +.theme-blog-post-page .avatar-list .avatar, +.blog-post-page .avatar-list .avatar { + padding: 0.75rem 1rem; + background: var(--ifm-color-emphasis-50); + border-left: 2px solid var(--om-secondary-cyan); + border-radius: 4px; +} + +[data-theme='dark'] .theme-blog-post-page .avatar-list .avatar, +[data-theme='dark'] .blog-post-page .avatar-list .avatar { + background: var(--ifm-color-emphasis-100); +} + +/* ── Blog sidebar (the post list on the left) ── */ +.theme-blog-sidebar-item-title, +.theme-blog-sidebar .sidebar-title, +nav[aria-label="Blog recent posts navigation"] > div:first-child, +nav.theme-blog-sidebar > div:first-child { + font-size: 0.9rem; + font-weight: 700; + letter-spacing: 0.01em; + color: var(--ifm-color-emphasis-800); + padding-bottom: 0.5rem; +} + +/* Year headings in sidebar ("2026", "2025") with short teal underline */ +nav.theme-blog-sidebar h3, +nav[aria-label*="blog"] h3 { + font-size: 1.1rem; + font-weight: 700; + color: var(--ifm-color-primary); + margin: 1.5rem 0 0.5rem; + padding-bottom: 0.4rem; + position: relative; +} + +nav.theme-blog-sidebar h3::after, +nav[aria-label*="blog"] h3::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 40px; + height: 2px; + background: var(--om-secondary-cyan); +} + +nav.theme-blog-sidebar h3:first-of-type, +nav[aria-label*="blog"] h3:first-of-type { + margin-top: 0; +} + +/* Sidebar links */ +nav.theme-blog-sidebar a, +nav[aria-label*="blog"] a { + font-size: 0.875rem; + color: var(--ifm-color-emphasis-700); + text-decoration: none; + padding: 0.4rem 0.5rem; + display: block; + border-radius: 4px; + border-left: 2px solid transparent; + transition: all 0.15s ease; +} + +nav.theme-blog-sidebar a:hover, +nav[aria-label*="blog"] a:hover { + background: var(--ifm-color-emphasis-100); + color: var(--ifm-color-primary); + border-left-color: var(--om-secondary-cyan); +} + +nav.theme-blog-sidebar a.menu__link--active, +nav.theme-blog-sidebar a[aria-current="page"], +nav[aria-label*="blog"] a[aria-current="page"] { + background: var(--ifm-color-emphasis-100); + color: var(--ifm-color-primary); + border-left-color: var(--om-secondary-cyan); + font-weight: 600; +} + +/* ── Right-side TOC (table of contents) ── */ +.theme-blog-post-page .table-of-contents, +.blog-post-page .table-of-contents { + font-size: 0.85rem; +} + +.theme-blog-post-page .table-of-contents__link--active, +.blog-post-page .table-of-contents__link--active { + color: var(--ifm-color-primary); + font-weight: 600; +} + +.theme-blog-post-page .table-of-contents .table-of-contents__link:hover, +.blog-post-page .table-of-contents .table-of-contents__link:hover { + color: var(--ifm-color-primary); +} + +/* ── Blog list page (the /blog index) ── */ +.blog-list-page article { + padding-bottom: 2rem; + margin-bottom: 2rem; + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.blog-list-page article:last-child { + border-bottom: none; +} + +.blog-list-page article h2 { + margin-bottom: 0.5rem; +} + +.blog-list-page article h2 a { + color: var(--ifm-color-primary); + text-decoration: none; +} + +.blog-list-page article h2 a:hover { + color: var(--ifm-color-primary-dark); + text-decoration: underline; + text-decoration-color: var(--om-secondary-cyan); + text-decoration-thickness: 2px; + text-underline-offset: 4px; +} + +/* "Read more" link */ +.blog-list-page .button--secondary, +.theme-blog-post-page .button--secondary { + background: transparent; + border: 1px solid var(--ifm-color-emphasis-300); + color: var(--ifm-color-primary); + font-size: 0.875rem; + font-weight: 500; + padding: 0.4rem 0.9rem; + border-radius: 4px; + transition: all 0.15s ease; +} + +.blog-list-page .button--secondary:hover, +.theme-blog-post-page .button--secondary:hover { + background: var(--ifm-color-primary); + color: white; + border-color: var(--ifm-color-primary); +} + +/* ── Tags ── */ +.theme-blog-post-page .badge, +.blog-list-page .badge, +.blog-post-page .badge { + background: var(--ifm-color-emphasis-100); + color: var(--ifm-color-emphasis-800); + border: 1px solid var(--ifm-color-emphasis-200); + font-weight: 500; + font-size: 0.75rem; + padding: 0.2rem 0.6rem; +} + +.theme-blog-post-page .badge:hover, +.blog-list-page .badge:hover { + background: var(--om-secondary-cyan); + color: var(--om-dark); + border-color: var(--om-secondary-cyan); +} + +/* ── Pagination ── */ +.pagination-nav__link { + border-radius: 6px; + border: 1px solid var(--ifm-color-emphasis-200); + transition: all 0.15s ease; +} + +.pagination-nav__link:hover { + border-color: var(--om-secondary-cyan); + background: var(--ifm-color-emphasis-50); +} + +.pagination-nav__label { + color: var(--ifm-color-primary); + font-weight: 600; +} + +/* ── Pull quote treatment for blockquotes inside posts ── */ +.theme-blog-post-page blockquote, +.blog-post-page blockquote { + border-left: 3px solid var(--om-secondary-cyan); + background: var(--ifm-color-emphasis-50); + padding: 1rem 1.25rem; + margin: 1.5rem 0; + border-radius: 0 4px 4px 0; + font-style: normal; + color: var(--ifm-color-emphasis-800); +} + +[data-theme='dark'] .theme-blog-post-page blockquote, +[data-theme='dark'] .blog-post-page blockquote { + background: var(--ifm-color-emphasis-100); } \ No newline at end of file