From 9317a3f5e7ce524aab9da73303b8e7a19146dc12 Mon Sep 17 00:00:00 2001 From: wompmacho Date: Sat, 23 May 2026 13:40:27 +0000 Subject: [PATCH] adding skills / agents / other to config --- ai/RULES.md | 22 + ai/skills/.DS_Store | Bin 0 -> 6148 bytes ai/skills/architecture_reviewer/SKILL.md | 22 + ai/skills/code_quality/SKILL.md | 23 + ai/skills/core/SKILL.md | 27 + ai/skills/docs_architecture/SKILL.md | 61 + ai/skills/go_expert/SKILL.md | 16 + ai/skills/go_expert/STYLE_GUIDE.md | 5446 ++++++++++++++++++++ ai/skills/grill-me/SKILL.md | 10 + ai/skills/handoff/SKILL.md | 15 + ai/skills/html_css_expert/SKILL.md | 15 + ai/skills/html_css_expert/STYLE_GUIDE.md | 668 +++ ai/skills/java_expert/SKILL.md | 15 + ai/skills/java_expert/STYLE_GUIDE.md | 779 +++ ai/skills/javascript_expert/SKILL.md | 16 + ai/skills/javascript_expert/STYLE_GUIDE.md | 2660 ++++++++++ ai/skills/json_expert/SKILL.md | 15 + ai/skills/json_expert/STYLE_GUIDE.md | 400 ++ ai/skills/lisp_expert/SKILL.md | 15 + ai/skills/lisp_expert/STYLE_GUIDE.md | 314 ++ ai/skills/markdown_expert/SKILL.md | 17 + ai/skills/markdown_expert/STYLE_GUIDE.md | 650 +++ ai/skills/shell_expert/SKILL.md | 16 + ai/skills/shell_expert/STYLE_GUIDE.md | 948 ++++ ai/skills/tdd/SKILL.md | 109 + ai/skills/tdd/deep-modules.md | 33 + ai/skills/tdd/interface-design.md | 31 + ai/skills/tdd/mocking.md | 59 + ai/skills/tdd/refactoring.md | 10 + ai/skills/tdd/tests.md | 61 + ai/skills/to-prd/SKILL.md | 76 + ai/skills/web-to-markdown/SKILL.md | 34 + ai/skills/workflow_management/SKILL.md | 25 + ai/skills/write-a-skill/SKILL.md | 117 + 34 files changed, 12725 insertions(+) create mode 100644 ai/RULES.md create mode 100644 ai/skills/.DS_Store create mode 100644 ai/skills/architecture_reviewer/SKILL.md create mode 100644 ai/skills/code_quality/SKILL.md create mode 100644 ai/skills/core/SKILL.md create mode 100644 ai/skills/docs_architecture/SKILL.md create mode 100644 ai/skills/go_expert/SKILL.md create mode 100644 ai/skills/go_expert/STYLE_GUIDE.md create mode 100644 ai/skills/grill-me/SKILL.md create mode 100644 ai/skills/handoff/SKILL.md create mode 100644 ai/skills/html_css_expert/SKILL.md create mode 100644 ai/skills/html_css_expert/STYLE_GUIDE.md create mode 100644 ai/skills/java_expert/SKILL.md create mode 100644 ai/skills/java_expert/STYLE_GUIDE.md create mode 100644 ai/skills/javascript_expert/SKILL.md create mode 100644 ai/skills/javascript_expert/STYLE_GUIDE.md create mode 100644 ai/skills/json_expert/SKILL.md create mode 100644 ai/skills/json_expert/STYLE_GUIDE.md create mode 100644 ai/skills/lisp_expert/SKILL.md create mode 100644 ai/skills/lisp_expert/STYLE_GUIDE.md create mode 100644 ai/skills/markdown_expert/SKILL.md create mode 100644 ai/skills/markdown_expert/STYLE_GUIDE.md create mode 100644 ai/skills/shell_expert/SKILL.md create mode 100644 ai/skills/shell_expert/STYLE_GUIDE.md create mode 100644 ai/skills/tdd/SKILL.md create mode 100644 ai/skills/tdd/deep-modules.md create mode 100644 ai/skills/tdd/interface-design.md create mode 100644 ai/skills/tdd/mocking.md create mode 100644 ai/skills/tdd/refactoring.md create mode 100644 ai/skills/tdd/tests.md create mode 100644 ai/skills/to-prd/SKILL.md create mode 100644 ai/skills/web-to-markdown/SKILL.md create mode 100644 ai/skills/workflow_management/SKILL.md create mode 100644 ai/skills/write-a-skill/SKILL.md diff --git a/ai/RULES.md b/ai/RULES.md new file mode 100644 index 0000000..cb2849e --- /dev/null +++ b/ai/RULES.md @@ -0,0 +1,22 @@ +## HARD MANDATE: PLAN BEFORE ACTION +- **No Unapproved State Changes:** You MUST NOT execute any tool that modifies the filesystem, runs shell commands (excluding read-only discovery like `ls` or `grep`), or changes system state without first presenting a detailed plan and receiving an explicit "Directive" (e.g., "proceed") from the user. This rule is absolute. REQUIRED! + +## Gemini Added Memories +- When working on the Hugo wiki, docs, or Caddy routing, immediately run the docs_architecture skill to understand the decoupled architecture, deployment logic, and known Docker/Caddy networking quirks. +- When writing Hugo/Blowfish Markdown front matter, always include 'author', 'date' (ISO-8601), 'lastmod' (YYYY-MM-DD), and 'tags' (list) at the bottom of the metadata block. +- Never perform manual builds or deployments of the Hugo sites to bypass the CI/CD pipeline. Always rely on the Gitea Actions workflows for deployment. +- Avoid making direct edits to Nginx Proxy Manager .conf files because it causes a desync with the Web UI database. Always prefer asking the user to make changes via the NPM Web UI instead. If a direct edit is absolutely necessary, always remind the user that the UI will be out of sync. +- When generating dates for Hugo/Blowfish front matter (or any timestamps), never use UTC (Z). Always use EST (America/New_York) timezone offsets (e.g., -05:00 for EST or -04:00 for EDT). +- For all Markdown files, always ensure there is exactly one empty newline immediately following any heading (e.g., # Heading, ## Subheading). +- Always present a detailed summary of planned changes (identifying specific files and the nature of the edits) before executing any modifications. You MUST explicitly ask the user for permission to proceed and wait for a "Directive" before acting. +- When creating callouts or admonitions in Hugo Markdown files, always use the `> [!TYPE]` blockquote syntax (supported by the `hugo-admonitions` render hook) instead of theme-specific shortcodes. + +## REQUIRED! Workflow +- **Evaluate Prompt:** For every user request, first decide if specific skills/agents (e.g., codebase_investigator) are required. REQUIRED! +- **Present Plan:** Always create a clear plan and present it to the user. Do not act until a "Directive" (e.g., "proceed") is given. REQUIRED! +- **Logging:** For every resolved prompt, append a log entry to the project directory. REQUIRED! + - Format: Use a Markdown table. + - Columns: `| Date (EST) | Prompt | Resolution Steps (Detailed Files/Paths/Tools) |`. + - Rule: Never remove or overwrite entries; only append. + - Timezone: Always use EST/EDT offsets. + - Confirm the project directory by asking the user. diff --git a/ai/skills/.DS_Store b/ai/skills/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d3610d196948ced8cb206206cfe52a43c0ec9f6d GIT binary patch literal 6148 zcmeH~QBJ}@5Qb-w2M8o!jERrEf;VUgp1=cC1Oyc*wt)K9qx;S7hCr}98e(S1?B7mj z{@wkz>9Qi?2HVNKNMA%+xMHJC^NZ@2d6rE@ZPc2K2 z;j5_c=#wMe8MZD=b3|D9`PN8s14r)pUPx2M%=fUOml?%Ne08kiCBAJoCR4x^xD*B0 zv(;LifYzDWNBD*sM zOo4x;fNKsPhXbY*_tuli>0X=QPq>=qRRQ-UT==b+T-}QAaBsvDy#UjQRX`$`{Sojo KSYrzOsRBQoTX&5B literal 0 HcmV?d00001 diff --git a/ai/skills/architecture_reviewer/SKILL.md b/ai/skills/architecture_reviewer/SKILL.md new file mode 100644 index 0000000..5b83fea --- /dev/null +++ b/ai/skills/architecture_reviewer/SKILL.md @@ -0,0 +1,22 @@ +--- +name: architecture-reviewer +description: Expert skill for reviewing system architecture, design patterns, and scalability using public industry standards. +--- + +# Architecture Reviewer Skill + +This skill provides high-level design oversight, ensuring systems are resilient, scalable, and follow modern architectural patterns (e.g., Microservices, Event-Driven Architecture, 12-factor apps). + +## Principles + +1. **Public Standard Alignment**: Use world-class architectural principles (e.g., AWS Well-Architected Framework, Google Cloud Architecture Framework - public-facing). +2. **Scalability & Resilience**: Evaluate designs for potential bottlenecks, single points of failure, and horizontal scalability. +3. **Interface Design**: Favor gRPC/Protocol Buffers (public) or RESTful APIs (standardized) for component communication. +4. **Security-First**: Ensure Zero Trust principles and data encryption are considered early in the planning. +5. **Simplicity over Complexity**: Strongly favor simple, well-understood patterns over complex, custom solutions. + +## Architecture Review Checklist +- Is the system decoupled? +- Are the data storage solutions appropriate for the access patterns? +- How is observability (monitoring/logging) handled using public tools? +- Does the design avoid vendor lock-in where possible? diff --git a/ai/skills/code_quality/SKILL.md b/ai/skills/code_quality/SKILL.md new file mode 100644 index 0000000..32906da --- /dev/null +++ b/ai/skills/code_quality/SKILL.md @@ -0,0 +1,23 @@ +--- +name: code_quality_expert +description: Foundational skill for enforcing general code quality standards, SOLID principles, and software engineering best practices. +--- + +# Code Quality Expert Skill + +This skill ensures that all code produced or reviewed by the agent adheres to high-quality software engineering standards, regardless of the target language. + +## Principles + +1. **SOLID Principles**: Ensure single responsibility, open-closed, Liskov substitution, interface segregation, and dependency inversion. +2. **DRY (Don't Repeat Yourself)**: Avoid redundant logic by abstracting it into reusable components. +3. **KISS (Keep It Simple, Stupid)**: Prioritize simple, readable code over clever, complex solutions. +4. **Meaningful Naming**: Use descriptive and consistent names for variables, functions, and classes. +5. **Testability**: Design code that is easy to test using unit, integration, and end-to-end tests. +6. **Style Guide Adherence**: Enforce language-specific style guides (e.g., Google Python Style Guide, PEP 8) in the public domain. + +## Code Quality Checklist +- Is the code readable and well-documented? +- Are there any potential performance or security risks? +- Is the error handling robust and appropriate? +- Are there any architectural violations? diff --git a/ai/skills/core/SKILL.md b/ai/skills/core/SKILL.md new file mode 100644 index 0000000..23399ea --- /dev/null +++ b/ai/skills/core/SKILL.md @@ -0,0 +1,27 @@ +--- +name: framework_core +description: General-purpose skill for maintaining the Gemini Workflow Framework and following its core rules. +--- +# Framework Core Skill + +This skill ensures that all Gemini interactions adhere to the rules defined in `GEMINI.md`. + +## Available Experts +- **Lisp**: `lisp_expert` +- **Go**: `go_expert` +- **HTML/CSS**: `html_css_expert` +- **JavaScript**: `javascript_expert` +- **Java**: `java_expert` +- **JSON**: `json_expert` +- **Markdown**: `markdown_expert` +- **Shell**: `shell_expert` +- **Code Quality**: `code_quality_expert` +- **Architecture**: `architecture_reviewer` + +## Guidelines + +1. **Rule Enforcement**: Before every major action, verify it against the rules in `GEMINI.md`. +2. **Mandatory Expert Loading**: For any task involving a specific language or domain, you MUST load and follow the corresponding expert skill. +3. **External Auditing**: Always search for public-facing alternatives to internal Google tools. +4. **Planning Discipline**: Maintain the "Planning Phase" until explicit user approval for implementation is received. +5. **Interactive Clarification**: Use the `ask_question` pattern if a user request deviates from the established workflow framework. diff --git a/ai/skills/docs_architecture/SKILL.md b/ai/skills/docs_architecture/SKILL.md new file mode 100644 index 0000000..9f04631 --- /dev/null +++ b/ai/skills/docs_architecture/SKILL.md @@ -0,0 +1,61 @@ +# Docs Architecture Skill + +This skill documents the exact technical architecture, routing, and deployment workflow for WompMacho's decoupled Hugo/Gitea/Caddy Wiki infrastructure. + +## 🏗️ 1. Core Architecture (Decoupled Repositories) + +The system is split into three independent Git repositories hosted on `git.wompmacho.com`: + +1. **Framework (`/srv/dev/hugo/wiki`)**: Repository `hugo-framework` + * **Purpose**: The central Hugo engine. Contains the Blowfish theme as a submodule (`themes/blowfish`) and the master `config/_default/params.toml`. + * **Rule**: NEVER place content (`.md` files) here. NEVER place environment-specific overrides (like hardcoded `editURLs` or top-level menus) in the global `params.toml`. + +2. **Public Wiki (`/srv/docs/public`)**: Repository `docs-public` + * **Purpose**: Public-facing markdown notes, assets (`/static`), and its own top-bar menu configuration (`config/_default/menus.en.toml`). + * **Deployment Target**: `/srv/caddy/sites/wiki` + * **Live URL**: `https://wiki.wompmacho.com` (proxied by NPM). + +3. **Private Wiki (`/srv/docs/private`)**: Repository `docs-private` + * **Purpose**: Personal, private markdown notes, assets (`/static`), and its own top-bar menu configuration (`config/_default/menus.en.toml`). + * **Deployment Target**: `/srv/caddy/sites/private-wiki` + * **Live URL**: `http://10.0.0.190:9897` (or `http://private` via NPM). + +## 💻 2. Local Development ("Live Link") + +When working on content, the user uses **Code-Server**. To see real-time updates without deploying, the `hugo-framework` repository uses symbolic links. + +* `content -> /srv/docs/public` +* `static -> /srv/docs/public/static` + +**Command to start local preview**: `cd /srv/dev/hugo/wiki && hugo server --bind 0.0.0.0 --appendPort=false --baseURL="/"` + +## 🚀 3. CI/CD Pipeline (Gitea Actions) + +Both `docs-public` and `docs-private` have a `.gitea/workflows/deploy.yaml` action. + +### The Build Process +1. **Checkout Content**: The runner checks out the markdown content. +2. **Clone Framework**: The runner natively clones the `hugo-framework` (including submodules) using `git clone --recurse-submodules https://git.wompmacho.com/wompmacho/hugo-framework.git`. +3. **Inject Content**: Markdown and static files are copied into the framework's workspace. +4. **Inject Dynamic Menus**: The `config/_default/menus.en.toml` is injected, automatically overriding the framework's top navigation bar for that specific site. +5. **Inject Edit Links (CRITICAL RULE)**: The "Edit this page" link on the bottom of posts MUST NOT be configured via a `params.toml` override, as Hugo replaces the entire file instead of merging it, destroying the Blowfish styling. + * **Fix**: It is injected dynamically via an Environment Variable right before the build command: + * `HUGO_PARAMS_ARTICLE_EDITURL="https://git.../src/branch/main" hugo --minify ...` +6. **Deploy**: The output `/public` folder is copied directly to the mounted `/deploy` volume. + +## 🌐 4. Web Server Routing (Nginx) + +The system relies on two extremely lightweight `nginx:alpine` containers defined in `gitea/docker-compose.yaml` to serve static files. They act as "dumb" file servers behind Nginx Proxy Manager (NPM), bypassing the complex Host header validation and 0-byte security issues inherent to Caddy. + +1. **Public (`public_wiki` - Port 9896)**: + * Mounts `/srv/caddy/sites/wiki` as read-only. + * NPM routes `wiki.wompmacho.com` to Docker Host IP on port `9896`. +2. **Private (`private_wiki` - Port 9897)**: + * Mounts `/srv/caddy/sites/private-wiki` as read-only. + * NPM routes `http://private` to Docker Host IP on port `9897`. + +## 🛠️ 5. Known Quirks and Formatting Rules + +* **Submodules**: Always use `git submodule add -b main ` and ensure the commit pointer is tracked. Do not copy theme files physically. +* **Blowfish Homepage**: If the homepage is blank, it means there are no recent posts. By default, Blowfish's `home` layout only lists files with `type: "article"` or `type: "posts"`. For generic folders, set `type: "page"` and `layout: "list"` in `_index.md`. +* **Frontmatter**: Blowfish expects standard Hugo frontmatter (`date:` as a string, `authors: ["Name"]` as an array). MkDocs-style nested YAML is incompatible and will crash the build. \ No newline at end of file diff --git a/ai/skills/go_expert/SKILL.md b/ai/skills/go_expert/SKILL.md new file mode 100644 index 0000000..0c65a6b --- /dev/null +++ b/ai/skills/go_expert/SKILL.md @@ -0,0 +1,16 @@ +--- +name: go_expert +description: Google Go Style Guide Expert. Covers Style Guide, Style Decisions, and Best Practices. +--- +# Go Expert + +Expert in Google Go Style Guide. + +## Core Mandate +Follow the local style guide: +- [Consolidated Style Guide](./STYLE_GUIDE.md) + +## Key Principles +- Simplicity and maintainability. +- Standard libraries over third-party alternatives where possible. +- Avoid complexity in APIs. diff --git a/ai/skills/go_expert/STYLE_GUIDE.md b/ai/skills/go_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..d53c2cd --- /dev/null +++ b/ai/skills/go_expert/STYLE_GUIDE.md @@ -0,0 +1,5446 @@ +styleguide +Go Style Guide + +https://google.github.io/styleguide/go/guide + +Overview | Guide | Decisions | Best practices + +Note: This is part of a series of documents that outline Go Style at Google. This document is normative and canonical. See the overview for more information. + +Style principles + +There are a few overarching principles that summarize how to think about writing readable Go code. The following are attributes of readable code, in order of importance: + +Clarity: The code’s purpose and rationale is clear to the reader. +Simplicity: The code accomplishes its goal in the simplest way possible. +Concision: The code has a high signal-to-noise ratio. +Maintainability: The code is written such that it can be easily maintained. +Consistency: The code is consistent with the broader Google codebase. + +Clarity + +The core goal of readability is to produce code that is clear to the reader. + +Clarity is primarily achieved with effective naming, helpful commentary, and efficient code organization. + +Clarity is to be viewed through the lens of the reader, not the author of the code. It is more important that code be easy to read than easy to write. Clarity in code has two distinct facets: + +What is the code actually doing? +Why is the code doing what it does? + +What is the code actually doing? + +Go is designed such that it should be relatively straightforward to see what the code is doing. In cases of uncertainty or where a reader may require prior knowledge in order to understand the code, it is worth investing time in order to make the code’s purpose clearer for future readers. For example, it may help to: + +Use more descriptive variable names +Add additional commentary +Break up the code with whitespace and comments +Refactor the code into separate functions/methods to make it more modular + +There is no one-size-fits-all approach here, but it is important to prioritize clarity when developing Go code. + +Why is the code doing what it does? + +The code’s rationale is often sufficiently communicated by the names of variables, functions, methods, or packages. Where it is not, it is important to add commentary. The “Why?” is especially important when the code contains nuances that a reader may not be familiar with, such as: + +A nuance in the language, e.g., a closure will be capturing a loop variable, but the closure is many lines away +A nuance of the business logic, e.g., an access control check that needs to distinguish between the actual user and someone impersonating a user + +An API might require care to use correctly. For example, a piece of code may be intricate and difficult to follow for performance reasons, or a complex sequence of mathematical operations may use type conversions in an unexpected way. In these cases and many more, it is important that accompanying commentary and documentation explain these aspects so that future maintainers don’t make a mistake and so that readers can understand the code without needing to reverse-engineer it. + +It is also important to be aware that some attempts to provide clarity (such as adding extra commentary) can actually obscure the code’s purpose by adding clutter, restating what the code already says, contradicting the code, or adding maintenance burden to keep the comments up-to-date. Allow the code to speak for itself (e.g., by making the symbol names themselves self-describing) rather than adding redundant comments. It is often better for comments to explain why something is done, not what the code is doing. + +The Google codebase is largely uniform and consistent. It is often the case that code that stands out (e.g., by using an unfamiliar pattern) is doing so for a good reason, typically for performance. Maintaining this property is important to make it clear to readers where they should focus their attention when reading a new piece of code. + +The standard library contains many examples of this principle in action. Among them: + +Maintainer comments in package sort. +Good runnable examples in the same package, which benefit both users (they show up in godoc) and maintainers (they run as part of tests). +strings.Cut is only four lines of code, but they improve the clarity and correctness of callsites. + +Simplicity + +Your Go code should be simple for those using, reading, and maintaining it. + +Go code should be written in the simplest way that accomplishes its goals, both in terms of behavior and performance. Within the Google Go codebase, simple code: + +Is easy to read from top to bottom +Does not assume that you already know what it is doing +Does not assume that you can memorize all of the preceding code +Does not have unnecessary levels of abstraction +Does not have names that call attention to something mundane +Makes the propagation of values and decisions clear to the reader +Has comments that explain why, not what, the code is doing to avoid future deviation +Has documentation that stands on its own +Has useful errors and useful test failures +May often be mutually exclusive with “clever” code + +Tradeoffs can arise between code simplicity and API usage simplicity. For example, it may be worthwhile to have the code be more complex so that the end user of the API may more easily call the API correctly. In contrast, it may also be worthwhile to leave a bit of extra work to the end user of the API so that the code remains simple and easy to understand. + +When code needs complexity, the complexity should be added deliberately. This is typically necessary if additional performance is required or where there are multiple disparate customers of a particular library or service. Complexity may be justified, but it should come with accompanying documentation so that clients and future maintainers are able to understand and navigate the complexity. This should be supplemented with tests and examples that demonstrate its correct usage, especially if there is both a “simple” and a “complex” way to use the code. + +This principle does not imply that complex code cannot or should not be written in Go or that Go code is not allowed to be complex. We strive for a codebase that avoids unnecessary complexity so that when complexity does appear, it indicates that the code in question requires care to understand and maintain. Ideally, there should be accompanying commentary that explains the rationale and identifies the care that should be taken. This often arises when optimizing code for performance; doing so often requires a more complex approach, like preallocating a buffer and reusing it throughout a goroutine lifetime. When a maintainer sees this, it should be a clue that the code in question is performance-critical, and that should influence the care that is taken when making future changes. If employed unnecessarily, on the other hand, this complexity is a burden on those who need to read or change the code in the future. + +If code turns out to be very complex when its purpose should be simple, this is often a signal to revisit the implementation to see if there is a simpler way to accomplish the same thing. + +Least mechanism + +Where there are several ways to express the same idea, prefer the one that uses the most standard tools. Sophisticated machinery often exists, but should not be employed without reason. It is easy to add complexity to code as needed, whereas it is much harder to remove existing complexity after it has been found to be unnecessary. + +Aim to use a core language construct (for example a channel, slice, map, loop, or struct) when sufficient for your use case. +If there isn’t one, look for a tool within the standard library (like an HTTP client or a template engine). +Finally, consider whether there is a core library in the Google codebase that is sufficient before introducing a new dependency or creating your own. + +As an example, consider production code that contains a flag bound to a variable with a default value which must be overridden in tests. Unless intending to test the program’s command-line interface itself (say, with os/exec), it is simpler and therefore preferable to override the bound value directly rather than by using flag.Set. + +Similarly, if a piece of code requires a set membership check, a boolean-valued map (e.g., map[string]bool) often suffices. Libraries that provide set-like types and functionality should only be used if more complicated operations are required that are impossible or overly complicated with a map. + +Concision + +Concise Go code has a high signal-to-noise ratio. It is easy to discern the relevant details, and the naming and structure guide the reader through these details. + +There are many things that can get in the way of surfacing the most salient details at any given time: + +Repetitive code +Extraneous syntax +Opaque names +Unnecessary abstraction +Whitespace + +Repetitive code especially obscures the differences between each nearly-identical section, and requires a reader to visually compare similar lines of code to find the changes. Table-driven testing is a good example of a mechanism that can concisely factor out the common code from the important details of each repetition, but the choice of which pieces to include in the table will have an impact on how easy the table is to understand. + +When considering multiple ways to structure code, it is worth considering which way makes important details the most apparent. + +Understanding and using common code constructions and idioms are also important for maintaining a high signal-to-noise ratio. For example, the following code block is very common in error handling, and the reader can quickly understand the purpose of this block. + +// Good: +if err := doSomething(); err != nil { + // ... +} + + +If code looks very similar to this but is subtly different, a reader may not notice the change. In cases like this, it is worth intentionally “boosting” the signal of the error check by adding a comment to call attention to it. + +// Good: +if err := doSomething(); err == nil { // if NO error + // ... +} + + +Maintainability + +Code is edited many more times than it is written. Readable code not only makes sense to a reader who is trying to understand how it works, but also to the programmer who needs to change it. Clarity is key. + +Maintainable code: + +Is easy for a future programmer to modify correctly +Has APIs that are structured so that they can grow gracefully +Is clear about the assumptions that it makes and chooses abstractions that map to the structure of the problem, not to the structure of the code +Avoids unnecessary coupling and doesn’t include features that are not used +Has a comprehensive test suite to ensure promised behaviors are maintained and important logic is correct, and the tests provide clear, actionable diagnostics in case of failure + +When using abstractions like interfaces and types which by definition remove information from the context in which they are used, it is important to ensure that they provide sufficient benefit. Editors and IDEs can connect directly to a method definition and show the corresponding documentation when a concrete type is used, but can only refer to an interface definition otherwise. Interfaces are a powerful tool, but come with a cost, since the maintainer may need to understand the specifics of the underlying implementation in order to correctly use the interface, which must be explained within the interface documentation or at the call-site. + +Maintainable code also avoids hiding important details in places that are easy to overlook. For example, in each of the following lines of code, the presence or lack of a single character is critical to understand the line: + +// Bad: +// The use of = instead of := can change this line completely. +if user, err = db.UserByID(userID); err != nil { + // ... +} + +// Bad: +// The ! in the middle of this line is very easy to miss. +leap := (year%4 == 0) && (!(year%100 == 0) || (year%400 == 0)) + + +Neither of these are incorrect, but both could be written in a more explicit fashion, or could have an accompanying comment that calls attention to the important behavior: + +// Good: +u, err := db.UserByID(userID) +if err != nil { + return fmt.Errorf("invalid origin user: %s", err) +} +user = u + +// Good: +// Gregorian leap years aren't just year%4 == 0. +// See https://en.wikipedia.org/wiki/Leap_year#Algorithm. +var ( + leap4 = year%4 == 0 + leap100 = year%100 == 0 + leap400 = year%400 == 0 +) +leap := leap4 && (!leap100 || leap400) + + +In the same way, a helper function that hides critical logic or an important edge-case could make it easy for a future change to fail to account for it properly. + +Predictable names are another feature of maintainable code. A user of a package or a maintainer of a piece of code should be able to predict the name of a variable, method, or function in a given context. Function parameters and receiver names for identical concepts should typically share the same name, both to keep documentation understandable and to facilitate refactoring code with minimal overhead. + +Maintainable code minimizes its dependencies (both implicit and explicit). Depending on fewer packages means fewer lines of code that can affect behavior. Avoiding dependencies on internal or undocumented behavior makes code less likely to impose a maintenance burden when those behaviors change in the future. + +When considering how to structure or write code, it is worth taking the time to think through ways in which the code may evolve over time. If a given approach is more conducive to easier and safer future changes, that is often a good trade-off, even if it means a slightly more complicated design. + +Consistency + +Consistent code is code that looks, feels, and behaves like similar code throughout the broader codebase, within the context of a team or package, and even within a single file. + +Consistency concerns do not override any of the principles above, but if a tie must be broken, it is often beneficial to break it in favor of consistency. + +Consistency within a package is often the most immediately important level of consistency. It can be very jarring if the same problem is approached in multiple ways throughout a package, or if the same concept has many names within a file. However, even this should not override documented style principles or global consistency. + +Core guidelines + +These guidelines collect the most important aspects of Go style that all Go code is expected to follow. We expect that these principles be learned and followed by the time readability is granted. These are not expected to change frequently, and new additions will have to clear a high bar. + +The guidelines below expand on the recommendations in Effective Go, which provide a common baseline for Go code across the entire community. + +Formatting + +All Go source files must conform to the format outputted by the gofmt tool. This format is enforced by a presubmit check in the Google codebase. Generated code should generally also be formatted (e.g., by using format.Source), as it is also browsable in Code Search. + +MixedCaps + +Go source code uses MixedCaps or mixedCaps (camel case) rather than underscores (snake case) when writing multi-word names. + +This applies even when it breaks conventions in other languages. For example, a constant is MaxLength (not MAX_LENGTH) if exported and maxLength (not max_length) if unexported. + +Local variables are considered unexported for the purpose of choosing the initial capitalization. + +Line length + +There is no fixed line length for Go source code. If a line feels too long, prefer refactoring instead of splitting it. If it is already as short as it is practical for it to be, the line should be allowed to remain long. + +Do not split a line: + +Before an indentation change (e.g., function declaration, conditional) +To make a long string (e.g., a URL) fit into multiple shorter lines + +Naming + +Naming is more art than science. In Go, names tend to be somewhat shorter than in many other languages, but the same general guidelines apply. Names should: + +Not feel repetitive when they are used +Take the context into consideration +Not repeat concepts that are already clear + +You can find more specific guidance on naming in decisions. + +Local consistency + +Where the style guide has nothing to say about a particular point of style, authors are free to choose the style that they prefer, unless the code in close proximity (usually within the same file or package, but sometimes within a team or project directory) has taken a consistent stance on the issue. + +Examples of valid local style considerations: + +Use of %s or %v for formatted printing of errors +Usage of buffered channels in lieu of mutexes + +Examples of invalid local style considerations: + +Line length restrictions for code +Use of assertion-based testing libraries + +If the local style disagrees with the style guide but the readability impact is limited to one file, it will generally be surfaced in a code review for which a consistent fix would be outside the scope of the CL in question. At that point, it is appropriate to file a bug to track the fix. + +If a change would worsen an existing style deviation, expose it in more API surfaces, expand the number of files in which the deviation is present, or introduce an actual bug, then local consistency is no longer a valid justification for violating the style guide for new code. In these cases, it is appropriate for the author to clean up the existing codebase in the same CL, perform a refactor in advance of the current CL, or find an alternative that at least does not make the local problem worse. + + +--- GO STYLE DECISIONS --- + + +styleguide +Go Style Decisions + +https://google.github.io/styleguide/go/decisions + +Overview | Guide | Decisions | Best practices + +Note: This is part of a series of documents that outline Go Style at Google. This document is normative but not canonical, and is subordinate to the core style guide. See the overview for more information. + +About + +This document contains style decisions intended to unify and provide standard guidance, explanations, and examples for the advice given by the Go readability mentors. + +This document is not exhaustive and will grow over time. In cases where the core style guide contradicts the advice given here, the style guide takes precedence, and this document should be updated accordingly. + +See the Overview for the full set of Go Style documents. + +The following sections have moved from style decisions to another part of the guide: + +MixedCaps: see guide#mixed-caps + +Formatting: see guide#formatting + +Line Length: see guide#line-length + +Naming + +See the naming section within the core style guide for overarching guidance on naming. The following sections provide further clarification on specific areas within naming. + +Underscores + +Names in Go should in general not contain underscores. There are three exceptions to this principle: + +Package names that are only imported by generated code may contain underscores. See package names for more detail around how to choose multi-word package names. +Test, Benchmark and Example function names within *_test.go files may include underscores. +Low-level libraries that interoperate with the operating system or cgo may reuse identifiers, as is done in syscall. This is expected to be very rare in most codebases. + +Note: Filenames of source code are not Go identifiers and do not have to follow these conventions. They may contain underscores. + +Package names + +In Go, package names must be concise and use only lowercase letters and numbers (e.g., k8s, oauth2). Multi-word package names should remain unbroken and in all lowercase (e.g., tabwriter instead of tabWriter, TabWriter, or tab_writer). + +Avoid selecting package names that are likely to be shadowed by commonly used local variable names. For example, usercount is a better package name than count, since count is a commonly used variable name. + +Go package names should not have underscores. If you need to import a package that does have one in its name (usually from generated or third party code), it must be renamed at import time to a name that is suitable for use in Go code. + +An exception to this is that package names that are only imported by generated code may contain underscores. Specific examples include: + +Using the _test suffix for unit tests that only exercise the exported API of a package (package testing calls these “black box tests”). For example, a package linkedlist must define its black box unit tests in a package named linkedlist_test (not linked_list_test) + +Using underscores and the _test suffix for packages that specify functional or integration tests. For example, a linked list service integration test could be named linked_list_service_test + +Using the _test suffix for package-level documentation examples + +Avoid uninformative package names like util, utility, common, helper, model, testhelper, and so on that would tempt users of the package to rename it when importing. See: + +Guidance on so-called “utility packages” +Go Tip #97: What’s in a Name +Go Tip #108: The Power of a Good Package Name + +When an imported package is renamed (e.g. import foopb "path/to/foo_go_proto"), the local name for the package must comply with the rules above, as the local name dictates how the symbols in the package are referenced in the file. If a given import is renamed in multiple files, particularly in the same or nearby packages, the same local name should be used wherever possible for consistency. + +See also: Go blog post about package names. + +Receiver names + +Receiver variable names must be: + +Short (usually one or two letters in length) +Abbreviations for the type itself +Applied consistently to every receiver for that type +Not an underscore; omit the name if it is unused +Long Name Better Name +func (tray Tray) func (t Tray) +func (info *ResearchInfo) func (ri *ResearchInfo) +func (this *ReportWriter) func (w *ReportWriter) +func (self *Scanner) func (s *Scanner) + +Constant names + +Constant names must use MixedCaps like all other names in Go. (Exported constants start with uppercase, while unexported constants start with lowercase.) This applies even when it breaks conventions in other languages. Constant names should not be a derivative of their values and should instead explain what the value denotes. + +// Good: +const MaxPacketSize = 512 + +const ( + ExecuteBit = 1 << iota + WriteBit + ReadBit +) + + +Do not use non-MixedCaps constant names or constants with a K prefix. + +// Bad: +const MAX_PACKET_SIZE = 512 +const kMaxBufferSize = 1024 +const KMaxUsersPergroup = 500 + + +Name constants based on their role, not their values. If a constant does not have a role apart from its value, then it is unnecessary to define it as a constant. + +// Bad: +const Twelve = 12 + +const ( + UserNameColumn = "username" + GroupColumn = "group" +) + + +Initialisms + +Words in names that are initialisms or acronyms (e.g., URL and NATO) should have the same case. URL should appear as URL or url (as in urlPony, or URLPony), never as Url. As a general rule, identifiers (e.g., ID and DB) should also be capitalized similar to their usage in English prose. + +In names with multiple initialisms (e.g. XMLAPI because it contains XML and API), each letter within a given initialism should have the same case, but each initialism in the name does not need to have the same case. +In names with an initialism containing a lowercase letter (e.g. DDoS, iOS, gRPC), the initialism should appear as it would in standard prose, unless you need to change the first letter for the sake of exportedness. In these cases, the entire initialism should be the same case (e.g. ddos, IOS, GRPC). +English Usage Scope Correct Incorrect +XML API Exported XMLAPI XmlApi, XMLApi, XmlAPI, XMLapi +XML API Unexported xmlAPI xmlapi, xmlApi +iOS Exported IOS Ios, IoS +iOS Unexported iOS ios +gRPC Exported GRPC Grpc +gRPC Unexported gRPC grpc +DDoS Exported DDoS DDOS, Ddos +DDoS Unexported ddos dDoS, dDOS +ID Exported ID Id +ID Unexported id iD +DB Exported DB Db +DB Unexported db dB +Txn Exported Txn TXN + +Getters + +Function and method names should not use a Get or get prefix, unless the underlying concept uses the word “get” (e.g. an HTTP GET). Prefer starting the name with the noun directly, for example use Counts over GetCounts. + +If the function involves performing a complex computation or executing a remote call, a different word like Compute or Fetch can be used in place of Get, to make it clear to a reader that the function call may take time and could block or fail. + +Variable names + +The general rule of thumb is that the length of a name should be proportional to the size of its scope and inversely proportional to the number of times that it is used within that scope. A variable created at file scope may require multiple words, whereas a variable scoped to a single inner block may be a single word or even just a character or two, to keep the code clear and avoid extraneous information. + +Here is a rough baseline. These numeric guidelines are not strict rules. Apply judgement based on context, clarity, and concision. + +A small scope is one in which one or two small operations are performed, say 1-7 lines. +A medium scope is a few small or one large operation, say 8-15 lines. +A large scope is one or a few large operations, say 15-25 lines. +A very large scope is anything that spans more than a page (say, more than 25 lines). + +A name that might be perfectly clear (e.g., c for a counter) within a small scope could be insufficient in a larger scope and would require clarification to remind the reader of its purpose further along in the code. A scope in which there are many variables, or variables that represent similar values or concepts, may necessitate longer variable names than the scope suggests. + +The specificity of the concept can also help to keep a variable’s name concise. For example, assuming there is only a single database in use, a short variable name like db that might normally be reserved for very small scopes may remain perfectly clear even if the scope is very large. In this case, a single word database is likely acceptable based on the size of the scope, but is not required as db is a very common shortening for the word with few alternate interpretations. + +The name of a local variable should reflect what it contains and how it is being used in the current context, rather than where the value originated. For example, it is often the case that the best local variable name is not the same as the struct or protocol buffer field name. + +In general: + +Single-word names like count or options are a good starting point. +Additional words can be added to disambiguate similar names, for example userCount and projectCount. +Do not simply drop letters to save typing. For example Sandbox is preferred over Sbx, particularly for exported names. +Omit types and type-like words from most variable names. +For a number, userCount is a better name than numUsers or usersInt. +For a slice, users is a better name than userSlice. +It is acceptable to include a type-like qualifier if there are two versions of a value in scope, for example you might have an input stored in ageString and use age for the parsed value. +Omit words that are clear from the surrounding context. For example, in the implementation of a UserCount method, a local variable called userCount is probably redundant; count, users, or even c are just as readable. + +Single-letter variable names + +Single-letter variable names can be a useful tool to minimize repetition, but can also make code needlessly opaque. Limit their use to instances where the full word is obvious and where it would be repetitive for it to appear in place of the single-letter variable. + +In general: + +For a method receiver variable, a one-letter or two-letter name is preferred. +Using familiar variable names for common types is often helpful: +r for an io.Reader or *http.Request +w for an io.Writer or http.ResponseWriter +Single-letter identifiers are acceptable as integer loop variables, particularly for indices (e.g., i) and coordinates (e.g., x and y). +Abbreviations can be acceptable loop identifiers when the scope is short, for example for _, n := range nodes { ... }. + +Repetition + +A piece of Go source code should avoid unnecessary repetition. One common source of this is repetitive names, which often include unnecessary words or repeat their context or type. Code itself can also be unnecessarily repetitive if the same or a similar code segment appears multiple times in close proximity. + +Repetitive naming can come in many forms, including: + +Package vs. exported symbol name + +When naming exported symbols, the name of the package is always visible outside your package, so redundant information between the two should be reduced or eliminated. If a package exports only one type and it is named after the package itself, the canonical name for the constructor is New if one is required. + +Examples: Repetitive Name -> Better Name + +widget.NewWidget -> widget.New +widget.NewWidgetWithName -> widget.NewWithName +db.LoadFromDatabase -> db.Load +goatteleportutil.CountGoatsTeleported -> gtutil.CountGoatsTeleported or goatteleport.Count +myteampb.MyTeamMethodRequest -> mtpb.MyTeamMethodRequest or myteampb.MethodRequest + +Variable name vs. type + +The compiler always knows the type of a variable, and in most cases it is also clear to the reader what type a variable is by how it is used. It is only necessary to clarify the type of a variable if its value appears twice in the same scope. + +Repetitive Name Better Name +var numUsers int var users int +var nameString string var name string +var primaryProject *Project var primary *Project + +If the value appears in multiple forms, this can be clarified either with an extra word like raw and parsed or with the underlying representation: + +// Good: +limitRaw := r.FormValue("limit") +limit, err := strconv.Atoi(limitRaw) + +// Good: +limitStr := r.FormValue("limit") +limit, err := strconv.Atoi(limitStr) + + +External context vs. local names + +Names that include information from their surrounding context often create extra noise without benefit. The package name, method name, type name, function name, import path, and even filename can all provide context that automatically qualifies all names within. + +// Bad: +// In package "ads/targeting/revenue/reporting" +type AdsTargetingRevenueReport struct{} + +func (p *Project) ProjectName() string + +// Good: +// In package "ads/targeting/revenue/reporting" +type Report struct{} + +func (p *Project) Name() string + +// Bad: +// In package "sqldb" +type DBConnection struct{} + +// Good: +// In package "sqldb" +type Connection struct{} + +// Bad: +// In package "ads/targeting" +func Process(in *pb.FooProto) *Report { + adsTargetingID := in.GetAdsTargetingID() +} + +// Good: +// In package "ads/targeting" +func Process(in *pb.FooProto) *Report { + id := in.GetAdsTargetingID() +} + + +Repetition should generally be evaluated in the context of the user of the symbol, rather than in isolation. For example, the following code has lots of names that may be fine in some circumstances, but redundant in context: + +// Bad: +func (db *DB) UserCount() (userCount int, err error) { + var userCountInt64 int64 + if dbLoadError := db.LoadFromDatabase("count(distinct users)", &userCountInt64); dbLoadError != nil { + return 0, fmt.Errorf("failed to load user count: %s", dbLoadError) + } + userCount = int(userCountInt64) + return userCount, nil +} + + +Instead, information about names that are clear from context or usage can often be omitted: + +// Good: +func (db *DB) UserCount() (int, error) { + var count int64 + if err := db.Load("count(distinct users)", &count); err != nil { + return 0, fmt.Errorf("failed to load user count: %s", err) + } + return int(count), nil +} + + +Commentary + +The conventions around commentary (which include what to comment, what style to use, how to provide runnable examples, etc.) are intended to support the experience of reading the documentation of a public API. See Effective Go for more information. + +The best practices document’s section on documentation conventions discusses this further. + +Best Practice: Use doc preview during development and code review to see whether the documentation and runnable examples are useful and are presented the way you expect them to be. + +Tip: Godoc uses very little special formatting; lists and code snippets should usually be indented to avoid linewrapping. Apart from indentation, decoration should generally be avoided. + +Comment line length + +There is no fixed line length for comments in Go. + +Long comment lines should be wrapped to ensure that source is readable in tools which do not perform automatic wrapping of comment lines. If you are uncertain where to wrap, 80 or 100 columns are common choices. However, this is not a hard cut-off; there are situations where breaking a long literal text is harmful. There is no requirement for the specific column width at which wrapping occurs. Aim to be consistent within a file. + +See this post from The Go Blog on documentation for more on commentary. + +# Good: +// This is a comment paragraph. +// The length of individual lines doesn't matter in Godoc; +// but the choice of wrapping makes it easy to read on narrow screens. +// +// Don't worry too much about the long URL: +// https://supercalifragilisticexpialidocious.example.com:8080/Animalia/Chordata/Mammalia/Rodentia/Geomyoidea/Geomyidae/ +// +// Similarly, if you have other information that is made awkward +// by too many line breaks, use your judgment and include a long line +// if it helps rather than hinders. + + +Avoid comments that fit large amounts of text onto a single line, which is a poor reader experience. + +# Bad: +// This is a comment paragraph. While some code editors and viewers will wrap the paragraph for the reader, others will display a very long line that will overflow most windows and require users to scroll horizontally. In addition, even on a screen capable of displaying the entire line, it is easier to read a narrower paragraph than very wide one. +// +// Don't worry too much about the long URL: +// https://supercalifragilisticexpialidocious.example.com:8080/Animalia/Chordata/Mammalia/Rodentia/Geomyoidea/Geomyidae/ + + +Doc comments + +All top-level exported names must have doc comments, as should unexported type or function declarations with unobvious behavior or meaning. These comments should be full sentences that begin with the name of the object being described. An article (“a”, “an”, “the”) can precede the name to make it read more naturally. + +// Good: +// A Request represents a request to run a command. +type Request struct { ... + +// Encode writes the JSON encoding of req to w. +func Encode(w io.Writer, req *Request) { ... + + +Doc comments appear in Godoc and are surfaced by IDEs, and therefore should be written for anyone using the package. + +A documentation comment applies to the following symbol, or the group of fields if it appears in a struct. + +// Good: +// Options configure the group management service. +type Options struct { + // General setup: + Name string + Group *FooGroup + + // Dependencies: + DB *sql.DB + + // Customization: + LargeGroupThreshold int // optional; default: 10 + MinimumMembers int // optional; default: 2 +} + + +Best Practice: If you have doc comments for unexported code, follow the same custom as if it were exported (namely, starting the comment with the unexported name). This makes it easy to export it later by simply replacing the unexported name with the newly-exported one across both comments and code. + +Comment sentences + +Comments that are complete sentences should be capitalized and punctuated like standard English sentences. (As an exception, it is okay to begin a sentence with an uncapitalized identifier name if it is otherwise clear. Such cases are probably best done only at the beginning of a paragraph.) + +Comments that are sentence fragments have no such requirements for punctuation or capitalization. + +Documentation comments should always be complete sentences, and as such should always be capitalized and punctuated. Simple end-of-line comments (especially for struct fields) can be simple phrases that assume the field name is the subject. + +// Good: +// A Server handles serving quotes from the collected works of Shakespeare. +type Server struct { + // BaseDir points to the base directory under which Shakespeare's works are stored. + // + // The directory structure is expected to be the following: + // {BaseDir}/manifest.json + // {BaseDir}/{name}/{name}-part{number}.txt + BaseDir string + + WelcomeMessage string // displayed when user logs in + ProtocolVersion string // checked against incoming requests + PageLength int // lines per page when printing (optional; default: 20) +} + + +Examples + +Packages should clearly document their intended usage. Try to provide a runnable example; examples show up in Godoc. Runnable examples belong in the test file, not the production source file. See this example (Godoc, source). + +If it isn’t feasible to provide a runnable example, example code can be provided within code comments. As with other code and command-line snippets in comments, it should follow standard formatting conventions. + +Named result parameters + +When naming parameters, consider how function signatures appear in Godoc. The name of the function itself and the type of the result parameters are often sufficiently clear. + +// Good: +func (n *Node) Parent1() *Node +func (n *Node) Parent2() (*Node, error) + + +If a function returns two or more parameters of the same type, adding names can be useful. + +// Good: +func (n *Node) Children() (left, right *Node, err error) + + +If the caller must take action on particular result parameters, naming them can help suggest what the action is: + +// Good: +// WithTimeout returns a context that will be canceled no later than d duration +// from now. +// +// The caller must arrange for the returned cancel function to be called when +// the context is no longer needed to prevent a resource leak. +func WithTimeout(parent Context, d time.Duration) (ctx Context, cancel func()) + + +In the code above, cancellation is a particular action a caller must take. However, were the result parameters written as (Context, func()) alone, it would be unclear what is meant by “cancel function”. + +Don’t use named result parameters when the names produce unnecessary repetition. + +// Bad: +func (n *Node) Parent1() (node *Node) +func (n *Node) Parent2() (node *Node, err error) + + +Don’t name result parameters in order to avoid declaring a variable inside the function. This practice results in unnecessary API verbosity at the cost of minor implementation brevity. + +Naked returns are acceptable only in a small function. Once it’s a medium-sized function, be explicit with your returned values. Similarly, do not name result parameters just because it enables you to use naked returns. Clarity is always more important than saving a few lines in your function. + +It is always acceptable to name a result parameter if its value must be changed in a deferred closure. + +Tip: Types can often be clearer than names in function signatures. GoTip #38: Functions as Named Types demonstrates this. + +In, WithTimeout above, the real code uses a CancelFunc instead of a raw func() in the result parameter list and requires little effort to document. + +Package comments + +Package comments must appear immediately above the package clause with no blank line between the comment and the package name. Example: + +// Good: +// Package math provides basic constants and mathematical functions. +// +// This package does not guarantee bit-identical results across architectures. +package math + + +There must be a single package comment per package. If a package is composed of multiple files, exactly one of the files should have a package comment. + +Comments for main packages have a slightly different form, where the name of the go_binary rule in the BUILD file takes the place of the package name. + +// Good: +// The seed_generator command is a utility that generates a Finch seed file +// from a set of JSON study configs. +package main + + +Other styles of comment are fine as long as the name of the binary is exactly as written in the BUILD file. When the binary name is the first word, capitalizing it is required even though it does not strictly match the spelling of the command-line invocation. + +// Good: +// Binary seed_generator ... +// Command seed_generator ... +// Program seed_generator ... +// The seed_generator command ... +// The seed_generator program ... +// Seed_generator ... + + +Tips: + +Example command-line invocations and API usage can be useful documentation. For Godoc formatting, indent the comment lines containing code. + +If there is no obvious primary file or if the package comment is extraordinarily long, it is acceptable to put the doc comment in a file named doc.go with only the comment and the package clause. + +Multiline comments can be used instead of multiple single-line comments. This is primarily useful if the documentation contains sections which may be useful to copy and paste from the source file, as with sample command-lines (for binaries) and template examples. + +// Good: +/* +The seed_generator command is a utility that generates a Finch seed file +from a set of JSON study configs. + + seed_generator *.json | base64 > finch-seed.base64 +*/ +package template + + +Comments intended for maintainers and that apply to the whole file are typically placed after import declarations. These are not surfaced in Godoc and are not subject to the rules above on package comments. + +Imports + +Import renaming + +Package imports shouldn’t normally be renamed, but there are cases where they must be renamed or where a rename improves readability. + +Local names for imported packages must follow the guidance around package naming, including the prohibition on the use of underscores and capital letters. Try to be consistent by always using the same local name for the same imported package. + +An imported package must be renamed to avoid a name collision with other imports. (A corollary of this is that good package names should not require renaming.) In the event of a name collision, prefer to rename the most local or project-specific import. + +Generated protocol buffer packages must be renamed to remove underscores from their names, and their local names must have a pb suffix. See proto and stub best practices for more information. + +// Good: +import ( + foosvcpb "path/to/package/foo_service_go_proto" +) + + +Lastly, an imported, non-autogenerated package can be renamed if it has an uninformative name (e.g. util or v1) Do this sparingly: do not rename the package if the code surrounding the use of the package conveys enough context. When possible, prefer refactoring the package itself with a more suitable name. + +// Good: +import ( + core "github.com/kubernetes/api/core/v1" + meta "github.com/kubernetes/apimachinery/pkg/apis/meta/v1beta1" +) + + +If you need to import a package whose name collides with a common local variable name that you want to use (e.g. url, ssh) and you wish to rename the package, the preferred way to do so is with the pkg suffix (e.g. urlpkg). Note that it is possible to shadow a package with a local variable; this rename is only necessary if the package still needs to be used when such a variable is in scope. + +Import grouping + +Imports should be organized into the following groups, in order: + +Standard library packages + +Other (project and vendored) packages + +Protocol Buffer imports (e.g., fpb "path/to/foo_go_proto") + +Import for side-effects (e.g., _ "path/to/package") + +// Good: +package main + +import ( + "fmt" + "hash/adler32" + "os" + + "github.com/dsnet/compress/flate" + "golang.org/x/text/encoding" + "google.golang.org/protobuf/proto" + + foopb "myproj/foo/proto/proto" + + _ "myproj/rpc/protocols/dial" + _ "myproj/security/auth/authhooks" +) + + +Import “blank” (import _) + +Packages that are imported only for their side effects (using the syntax import _ "package") may only be imported in a main package, or in tests that require them. + +Some examples of such packages include: + +time/tzdata + +image/jpeg in image processing code + +Avoid blank imports in library packages, even if the library indirectly depends on them. Constraining side-effect imports to the main package helps control dependencies, and makes it possible to write tests that rely on a different import without conflict or wasted build costs. + +The following are the only exceptions to this rule: + +You may use a blank import to bypass the check for disallowed imports in the nogo static checker. + +You may use a blank import of the embed package in a source file which uses the //go:embed compiler directive. + +Tip: If you create a library package that indirectly depends on a side-effect import in production, document the intended usage. + +Import “dot” (import .) + +The import . form is a language feature that allows bringing identifiers exported from another package to the current package without qualification. See the language spec for more. + +Do not use this feature in the Google codebase; it makes it harder to tell where the functionality is coming from. + +// Bad: +package foo_test + +import ( + "bar/testutil" // also imports "foo" + . "foo" +) + +var myThing = Bar() // Bar defined in package foo; no qualification needed. + +// Good: +package foo_test + +import ( + "bar/testutil" // also imports "foo" + "foo" +) + +var myThing = foo.Bar() + + +Errors + +Returning errors + +Use error to signal that a function can fail. By convention, error is the last result parameter. + +// Good: +func Good() error { /* ... */ } + + +Returning a nil error is the idiomatic way to signal a successful operation that could otherwise fail. If a function returns an error, callers must treat all non-error return values as unspecified unless explicitly documented otherwise. Commonly, the non-error return values are their zero values, but this cannot be assumed. + +// Good: +func GoodLookup() (*Result, error) { + // ... + if err != nil { + return nil, err + } + return res, nil +} + + +Exported functions that return errors should return them using the error type. Concrete error types are susceptible to subtle bugs: a concrete nil pointer can get wrapped into an interface and thus become a non-nil value (see the Go FAQ entry on the topic). + +// Bad: +func Bad() *os.PathError { /*...*/ } + + +Tip: A function that takes a context.Context argument should usually return an error so that the caller can determine if the context was cancelled while the function was running. + +Error strings + +Error strings should not be capitalized (unless beginning with an exported name, a proper noun or an acronym) and should not end with punctuation. This is because error strings usually appear within other context before being printed to the user. + +// Bad: +err := fmt.Errorf("Something bad happened.") + +// Good: +err := fmt.Errorf("something bad happened") + + +On the other hand, the style for the full displayed message (logging, test failure, API response, or other UI) depends, but should typically be capitalized. + +// Good: +log.Infof("Operation aborted: %v", err) +log.Errorf("Operation aborted: %v", err) +t.Errorf("Op(%q) failed unexpectedly; err=%v", args, err) + + +Handle errors + +Code that encounters an error should make a deliberate choice about how to handle it. It is not usually appropriate to discard errors using _ variables. If a function returns an error, do one of the following: + +Handle and address the error immediately. +Return the error to the caller. +In exceptional situations, call log.Fatal or (if absolutely necessary) panic. + +Note: log.Fatalf is not the standard library log. See [#logging]. + +In the rare circumstance where it is appropriate to ignore or discard an error (for example a call to (*bytes.Buffer).Write that is documented to never fail), an accompanying comment should explain why this is safe. + +// Good: +var b *bytes.Buffer + +n, _ := b.Write(p) // never returns a non-nil error + + +For more discussion and examples of error handling, see Effective Go and best practices. + +In-band errors + +In C and similar languages, it is common for functions to return values like -1, null, or the empty string to signal errors or missing results. This is known as in-band error handling. + +// Bad: +// Lookup returns the value for key or -1 if there is no mapping for key. +func Lookup(key string) int + + +Failing to check for an in-band error value can lead to bugs and can attribute errors to the wrong function. + +// Bad: +// The following line returns an error that Parse failed for the input value, +// whereas the failure was that there is no mapping for missingKey. +return Parse(Lookup(missingKey)) + + +Go’s support for multiple return values provides a better solution (see the Effective Go section on multiple returns). Instead of requiring clients to check for an in-band error value, a function should return an additional value to indicate whether its other return values are valid. This return value may be an error or a boolean when no explanation is needed, and should be the final return value. + +// Good: +// Lookup returns the value for key or ok=false if there is no mapping for key. +func Lookup(key string) (value string, ok bool) + + +This API prevents the caller from incorrectly writing Parse(Lookup(key)) which causes a compile-time error, since Lookup(key) has 2 outputs. + +Returning errors in this way encourages more robust and explicit error handling: + +// Good: +value, ok := Lookup(key) +if !ok { + return fmt.Errorf("no value for %q", key) +} +return Parse(value) + + +Some standard library functions, like those in package strings, return in-band error values. This greatly simplifies string-manipulation code at the cost of requiring more diligence from the programmer. In general, Go code in the Google codebase should return additional values for errors. + +Indent error flow + +Handle errors before proceeding with the rest of your code. This improves the readability of the code by enabling the reader to find the normal path quickly. This same logic applies to any block which tests a condition then ends in a terminal condition (e.g., return, panic, log.Fatal). + +Code that runs if the terminal condition is not met should appear after the if block, and should not be indented in an else clause. + +// Good: +if err != nil { + // error handling + return // or continue, etc. +} +// normal code + +// Bad: +if err != nil { + // error handling +} else { + // normal code that looks abnormal due to indentation +} + + +Tip: If you are using a variable for more than a few lines of code, it is generally not worth using the if-with-initializer style. In these cases, it is usually better to move the declaration out and use a standard if statement: + +// Good: +x, err := f() +if err != nil { + // error handling + return +} +// lots of code that uses x +// across multiple lines + +// Bad: +if x, err := f(); err != nil { + // error handling + return +} else { + // lots of code that uses x + // across multiple lines +} + + +See Go Tip #1: Line of Sight and TotT: Reduce Code Complexity by Reducing Nesting for more details. + +Language + +Literal formatting + +Go has an exceptionally powerful composite literal syntax, with which it is possible to express deeply-nested, complicated values in a single expression. Where possible, this literal syntax should be used instead of building values field-by-field. The gofmt formatting for literals is generally quite good, but there are some additional rules for keeping these literals readable and maintainable. + +Field names + +Struct literals must specify field names for types defined outside the current package. + +Include field names for types from other packages. + +// Good: +// https://pkg.go.dev/encoding/csv#Reader +r := csv.Reader{ + Comma: ',', + Comment: '#', + FieldsPerRecord: 4, +} + + +The position of fields in a struct and the full set of fields (both of which are necessary to get right when field names are omitted) are not usually considered to be part of a struct’s public API; specifying the field name is needed to avoid unnecessary coupling. + +// Bad: +r := csv.Reader{',', '#', 4, false, false, false, false} + + +For package-local types, field names are optional. + +// Good: +okay := Type{42} +also := internalType{4, 2} + + +Field names should still be used if it makes the code clearer, and it is very common to do so. For example, a struct with a large number of fields should almost always be initialized with field names. + +// Good: +okay := StructWithLotsOfFields{ + field1: 1, + field2: "two", + field3: 3.14, + field4: true, +} + + +Matching braces + +The closing half of a brace pair should always appear on a line with the same amount of indentation as the opening brace. One-line literals necessarily have this property. When the literal spans multiple lines, maintaining this property keeps the brace matching for literals the same as brace matching for common Go syntactic constructs like functions and if statements. + +The most common mistake in this area is putting the closing brace on the same line as a value in a multi-line struct literal. In these cases, the line should end with a comma and the closing brace should appear on the next line. + +// Good: +good := []*Type{{Key: "value"}} + +// Good: +good := []*Type{ + {Key: "multi"}, + {Key: "line"}, +} + +// Bad: +bad := []*Type{ + {Key: "multi"}, + {Key: "line"}} + +// Bad: +bad := []*Type{ + { + Key: "value"}, +} + + +Cuddled braces + +Dropping whitespace between braces (aka “cuddling” them) for slice and array literals is only permitted when both of the following are true. + +The indentation matches +The inner values are also literals or proto builders (i.e. not a variable or other expression) +// Good: +good := []*Type{ + { // Not cuddled + Field: "value", + }, + { + Field: "value", + }, +} + +// Good: +good := []*Type{{ // Cuddled correctly + Field: "value", +}, { + Field: "value", +}} + +// Good: +good := []*Type{ + first, // Can't be cuddled + {Field: "second"}, +} + +// Good: +okay := []*pb.Type{pb.Type_builder{ + Field: "first", // Proto Builders may be cuddled to save vertical space +}.Build(), pb.Type_builder{ + Field: "second", +}.Build()} + +// Bad: +bad := []*Type{ + first, + { + Field: "second", + }} + + +Repeated type names + +Repeated type names may be omitted from slice and map literals. This can be helpful in reducing clutter. A reasonable occasion for repeating the type names explicitly is when dealing with a complex type that is not common in your project, when the repetitive type names are on lines that are far apart and can remind the reader of the context. + +// Good: +good := []*Type{ + {A: 42}, + {A: 43}, +} + +// Bad: +repetitive := []*Type{ + &Type{A: 42}, + &Type{A: 43}, +} + +// Good: +good := map[Type1]*Type2{ + {A: 1}: {B: 2}, + {A: 3}: {B: 4}, +} + +// Bad: +repetitive := map[Type1]*Type2{ + Type1{A: 1}: &Type2{B: 2}, + Type1{A: 3}: &Type2{B: 4}, +} + + +Tip: If you want to remove repetitive type names in struct literals, you can run gofmt -s. + +Zero-value fields + +Zero-value fields may be omitted from struct literals when clarity is not lost as a result. + +Well-designed APIs often employ zero-value construction for enhanced readability. For example, omitting the three zero-value fields from the following struct draws attention to the only option that is being specified. + +// Bad: +import ( + "github.com/golang/leveldb" + "github.com/golang/leveldb/db" +) + +ldb := leveldb.Open("/my/table", &db.Options{ + BlockSize: 1<<16, + ErrorIfDBExists: true, + + // These fields all have their zero values. + BlockRestartInterval: 0, + Comparer: nil, + Compression: nil, + FileSystem: nil, + FilterPolicy: nil, + MaxOpenFiles: 0, + WriteBufferSize: 0, + VerifyChecksums: false, +}) + +// Good: +import ( + "github.com/golang/leveldb" + "github.com/golang/leveldb/db" +) + +ldb := leveldb.Open("/my/table", &db.Options{ + BlockSize: 1<<16, + ErrorIfDBExists: true, +}) + + +Structs within table-driven tests often benefit from explicit field names, especially when the test struct is not trivial. This allows the author to omit the zero-valued fields entirely when the fields in question are not related to the test case. For example, successful test cases should omit any error-related or failure-related fields. In cases where the zero value is necessary to understand the test case, such as testing for zero or nil inputs, the field names should be specified. + +Concise + +tests := []struct { + input string + wantPieces []string + wantErr error +}{ + { + input: "1.2.3.4", + wantPieces: []string{"1", "2", "3", "4"}, + }, + { + input: "hostname", + wantErr: ErrBadHostname, + }, +} + + +Explicit + +tests := []struct { + input string + wantIPv4 bool + wantIPv6 bool + wantErr bool +}{ + { + input: "1.2.3.4", + wantIPv4: true, + wantIPv6: false, + }, + { + input: "1:2::3:4", + wantIPv4: false, + wantIPv6: true, + }, + { + input: "hostname", + wantIPv4: false, + wantIPv6: false, + wantErr: true, + }, +} + + +Nil slices + +For most purposes, there is no functional difference between nil and the empty slice. Built-in functions like len and cap behave as expected on nil slices. + +// Good: +import "fmt" + +var s []int // nil + +fmt.Println(s) // [] +fmt.Println(len(s)) // 0 +fmt.Println(cap(s)) // 0 +for range s {...} // no-op + +s = append(s, 42) +fmt.Println(s) // [42] + + +If you declare an empty slice as a local variable (especially if it can be the source of a return value), prefer the nil initialization to reduce the risk of bugs by callers. + +// Good: +var t []string + +// Bad: +t := []string{} + + +Do not create APIs that force their clients to make distinctions between nil and the empty slice. + +// Good: +// Ping pings its targets. +// Returns hosts that successfully responded. +func Ping(hosts []string) ([]string, error) { ... } + +// Bad: +// Ping pings its targets and returns a list of hosts +// that successfully responded. Can be empty if the input was empty. +// nil signifies that a system error occurred. +func Ping(hosts []string) []string { ... } + + +When designing interfaces, avoid making a distinction between a nil slice and a non-nil, zero-length slice, as this can lead to subtle programming errors. This is typically accomplished by using len to check for emptiness, rather than == nil. + +This implementation accepts both nil and zero-length slices as “empty”: + +// Good: +// describeInts describes s with the given prefix, unless s is empty. +func describeInts(prefix string, s []int) { + if len(s) == 0 { + return + } + fmt.Println(prefix, s) +} + + +Instead of relying on the distinction as a part of the API: + +// Bad: +func maybeInts() []int { /* ... */ } + +// describeInts describes s with the given prefix; pass nil to skip completely. +func describeInts(prefix string, s []int) { + // The behavior of this function unintentionally changes depending on what + // maybeInts() returns in 'empty' cases (nil or []int{}). + if s == nil { + return + } + fmt.Println(prefix, s) +} + +describeInts("Here are some ints:", maybeInts()) + + +See in-band errors for further discussion. + +Indentation confusion + +Avoid introducing a line break if it would align the rest of the line with an indented code block. If this is unavoidable, leave a space to separate the code in the block from the wrapped line. + +// Bad: +if longCondition1 && longCondition2 && + // Conditions 3 and 4 have the same indentation as the code within the if. + longCondition3 && longCondition4 { + log.Info("all conditions met") +} + + +See the following sections for specific guidelines and examples: + +Function formatting +Conditionals and loops +Literal formatting + +Function formatting + +The signature of a function or method declaration should remain on a single line to avoid indentation confusion. + +Function argument lists can make some of the longest lines in a Go source file. However, they precede a change in indentation, and therefore it is difficult to break the line in a way that does not make subsequent lines look like part of the function body in a confusing way: + +// Bad: +func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string, + foo4, foo5, foo6 int) { + foo7 := bar(foo1) + // ... +} + + +See best practices for a few options for shortening the call sites of functions that would otherwise have many arguments. + +Lines can often be shortened by factoring out local variables. + +// Good: +local := helper(some, parameters, here) +good := foo.Call(list, of, parameters, local) + + +Similarly, function and method calls should not be separated based solely on line length. + +// Good: +good := foo.Call(long, list, of, parameters, all, on, one, line) + +// Bad: +bad := foo.Call(long, list, of, parameters, + with, arbitrary, line, breaks) + + +Avoid adding inline comments to specific function arguments where possible. Instead, use an option struct or add more detail to the function documentation. + +// Good: +good := server.New(ctx, server.Options{Port: 42}) + +// Bad: +bad := server.New( + ctx, + 42, // Port +) + + +If the API cannot be changed or if the local call is unusual (whether or not the call is too long), it is always permissible to add line breaks if it aids in understanding the call. + +// Good: +canvas.RenderHeptagon(fillColor, + x0, y0, vertexColor0, + x1, y1, vertexColor1, + x2, y2, vertexColor2, + x3, y3, vertexColor3, + x4, y4, vertexColor4, + x5, y5, vertexColor5, + x6, y6, vertexColor6, +) + + +Note that the lines in the above example are not wrapped at a specific column boundary but are grouped based on vertex coordinates and color. + +Long string literals within functions should not be broken for the sake of line length. For functions that include such strings, a line break can be added after the string format, and the arguments can be provided on the next or subsequent lines. The decision about where the line breaks should go is best made based on semantic groupings of inputs, rather than based purely on line length. + +// Good: +log.Warningf("Database key (%q, %d, %q) incompatible in transaction started by (%q, %d, %q)", + currentCustomer, currentOffset, currentKey, + txCustomer, txOffset, txKey) + +// Bad: +log.Warningf("Database key (%q, %d, %q) incompatible in"+ + " transaction started by (%q, %d, %q)", + currentCustomer, currentOffset, currentKey, txCustomer, + txOffset, txKey) + + +Conditionals and loops + +An if statement should not be line broken; multi-line if clauses can lead to indentation confusion. + +// Bad: +// The second if statement is aligned with the code within the if block, causing +// indentation confusion. +if db.CurrentStatusIs(db.InTransaction) && + db.ValuesEqual(db.TransactionKey(), row.Key()) { + return db.Errorf(db.TransactionError, "query failed: row (%v): key does not match transaction key", row) +} + + +If the short-circuit behavior is not required, the boolean operands can be extracted directly: + +// Good: +inTransaction := db.CurrentStatusIs(db.InTransaction) +keysMatch := db.ValuesEqual(db.TransactionKey(), row.Key()) +if inTransaction && keysMatch { + return db.Error(db.TransactionError, "query failed: row (%v): key does not match transaction key", row) +} + + +There may also be other locals that can be extracted, especially if the conditional is already repetitive: + +// Good: +uid := user.GetUniqueUserID() +if db.UserIsAdmin(uid) || db.UserHasPermission(uid, perms.ViewServerConfig) || db.UserHasPermission(uid, perms.CreateGroup) { + // ... +} + +// Bad: +if db.UserIsAdmin(user.GetUniqueUserID()) || db.UserHasPermission(user.GetUniqueUserID(), perms.ViewServerConfig) || db.UserHasPermission(user.GetUniqueUserID(), perms.CreateGroup) { + // ... +} + + +if statements that contain closures or multi-line struct literals should ensure that the braces match to avoid indentation confusion. + +// Good: +if err := db.RunInTransaction(func(tx *db.TX) error { + return tx.Execute(userUpdate, x, y, z) +}); err != nil { + return fmt.Errorf("user update failed: %s", err) +} + +// Good: +if _, err := client.Update(ctx, &upb.UserUpdateRequest{ + ID: userID, + User: user, +}); err != nil { + return fmt.Errorf("user update failed: %s", err) +} + + +Similarly, don’t try inserting artificial linebreaks into for statements. You can always let the line simply be long if there is no elegant way to refactor it: + +// Good: +for i, max := 0, collection.Size(); i < max && !collection.HasPendingWriters(); i++ { + // ... +} + + +Often, though, there is: + +// Good: +for i, max := 0, collection.Size(); i < max; i++ { + if collection.HasPendingWriters() { + break + } + // ... +} + + +switch and case statements should also remain on a single line. + +// Good: +switch good := db.TransactionStatus(); good { +case db.TransactionStarting, db.TransactionActive, db.TransactionWaiting: + // ... +case db.TransactionCommitted, db.NoTransaction: + // ... +default: + // ... +} + +// Bad: +switch bad := db.TransactionStatus(); bad { +case db.TransactionStarting, + db.TransactionActive, + db.TransactionWaiting: + // ... +case db.TransactionCommitted, + db.NoTransaction: + // ... +default: + // ... +} + + +If the line is excessively long, indent all cases and separate them with a blank line to avoid indentation confusion: + +// Good: +switch db.TransactionStatus() { +case + db.TransactionStarting, + db.TransactionActive, + db.TransactionWaiting, + db.TransactionCommitted: + + // ... +case db.NoTransaction: + // ... +default: + // ... +} + + +In conditionals comparing a variable to a constant, place the variable value on the left hand side of the equality operator: + +// Good: +if result == "foo" { + // ... +} + + +Instead of the less clear phrasing where the constant comes first (“Yoda style conditionals”): + +// Bad: +if "foo" == result { + // ... +} + + +Copying + +To avoid unexpected aliasing and similar bugs, be careful when copying a struct from another package. For example, synchronization objects such as sync.Mutex must not be copied. + +The bytes.Buffer type contains a []byte slice and, as an optimization for small strings, a small byte array to which the slice may refer. If you copy a Buffer, the slice in the copy may alias the array in the original, causing subsequent method calls to have surprising effects. + +In general, do not copy a value of type T if its methods are associated with the pointer type, *T. + +// Bad: +b1 := bytes.Buffer{} +b2 := b1 + + +Invoking a method that takes a value receiver can hide the copy. When you author an API, you should generally take and return pointer types if your structs contain fields that should not be copied. + +These are acceptable: + +// Good: +type Record struct { + buf bytes.Buffer + // other fields omitted +} + +func New() *Record {...} + +func (r *Record) Process(...) {...} + +func Consumer(r *Record) {...} + + +But these are usually wrong: + +// Bad: +type Record struct { + buf bytes.Buffer + // other fields omitted +} + + +func (r Record) Process(...) {...} // Makes a copy of r.buf + +func Consumer(r Record) {...} // Makes a copy of r.buf + + +This guidance also applies to copying sync.Mutex. + +Don’t panic + +Do not use panic for normal error handling. Instead, use error and multiple return values. See the Effective Go section on errors. + +Within package main and initialization code, consider log.Exit for errors that should terminate the program (e.g., invalid configuration), as in many of these cases a stack trace will not help the reader. Please note that log.Exit calls os.Exit and any deferred functions will not be run. + +For errors that indicate “impossible” conditions, namely bugs that should always be caught during code review and/or testing, a function may reasonably return an error or call log.Fatal. + +Also see when panic is acceptable. + +Note: log.Fatalf is not the standard library log. See [#logging]. + +Must functions + +Setup helper functions that stop the program on failure follow the naming convention MustXYZ (or mustXYZ). In general, they should only be called early on program startup, not on things like user input where normal Go error handling is preferred. + +This often comes up for functions called to initialize package-level variables exclusively at package initialization time (e.g. template.Must and regexp.MustCompile). + +// Good: +func MustParse(version string) *Version { + v, err := Parse(version) + if err != nil { + panic(fmt.Sprintf("MustParse(%q) = _, %v", version, err)) + } + return v +} + +// Package level "constant". If we wanted to use `Parse`, we would have had to +// set the value in `init`. +var DefaultVersion = MustParse("1.2.3") + + +The same convention may be used in test helpers that only stop the current test (using t.Fatal). Such helpers are often convenient in creating test values, for example in struct fields of table driven tests, as functions that return errors cannot be directly assigned to a struct field. + +// Good: +func mustMarshalAny(t *testing.T, m proto.Message) *anypb.Any { + t.Helper() + any, err := anypb.New(m) + if err != nil { + t.Fatalf("mustMarshalAny(t, m) = %v; want %v", err, nil) + } + return any +} + +func TestCreateObject(t *testing.T) { + tests := []struct{ + desc string + data *anypb.Any + }{ + { + desc: "my test case", + // Creating values directly within table driven test cases. + data: mustMarshalAny(t, mypb.Object{}), + }, + // ... + } + // ... +} + + +In both of these cases, the value of this pattern is that the helpers can be called in a “value” context. These helpers should not be called in places where it’s difficult to ensure an error would be caught or in a context where an error should be checked (e.g., in many request handlers). For constant inputs, this allows tests to easily ensure that the Must arguments are well-formed, and for non-constant inputs it permits tests to validate that errors are properly handled or propagated. + +Where Must functions are used in a test, they should generally be marked as a test helper and call t.Fatal on error (see error handling in test helpers for more considerations of using that). + +They should not be used when ordinary error handling is possible (including with some refactoring): + +// Bad: +func Version(o *servicepb.Object) (*version.Version, error) { + // Return error instead of using Must functions. + v := version.MustParse(o.GetVersionString()) + return dealiasVersion(v) +} + + +Goroutine lifetimes + +When you spawn goroutines, make it clear when or whether they exit. + +Goroutines can leak by blocking on channel sends or receives. The garbage collector will not terminate a goroutine blocked on a channel even if no other goroutine has a reference to the channel. + +Even when goroutines do not leak, leaving them in-flight when they are no longer needed can cause other subtle and hard-to-diagnose problems. Sending on a channel that has been closed causes a panic. + +// Bad: +ch := make(chan int) +ch <- 42 +close(ch) +ch <- 13 // panic + + +Modifying still-in-use inputs “after the result isn’t needed” can lead to data races. Leaving goroutines in-flight for arbitrarily long can lead to unpredictable memory usage. + +Concurrent code should be written such that the goroutine lifetimes are obvious. Typically this will mean keeping synchronization-related code constrained within the scope of a function and factoring out the logic into synchronous functions. If the concurrency is still not obvious, it is important to document when and why the goroutines exit. + +Code that follows best practices around context usage often helps make this clear. It is conventionally managed with a context.Context: + +// Good: +func (w *Worker) Run(ctx context.Context) error { + var wg sync.WaitGroup + // ... + for item := range w.q { + // process returns at latest when the context is cancelled. + wg.Add(1) + go func() { + defer wg.Done() + process(ctx, item) + }() + } + // ... + wg.Wait() // Prevent spawned goroutines from outliving this function. +} + + +There are other variants of the above that use raw signal channels like chan struct{}, synchronized variables, condition variables, and more. The important part is that the goroutine’s end is evident for subsequent maintainers. + +In contrast, the following code is careless about when its spawned goroutines finish: + +// Bad: +func (w *Worker) Run() { + // ... + for item := range w.q { + // process returns when it finishes, if ever, possibly not cleanly + // handling a state transition or termination of the Go program itself. + go process(item) + } + // ... +} + + +This code may look OK, but there are several underlying problems: + +The code probably has undefined behavior in production, and the program may not terminate cleanly, even if the operating system releases the resources. + +The code is difficult to test meaningfully due to the code’s indeterminate lifecycle. + +The code may leak resources as described above. + +See also: + +Never start a goroutine without knowing how it will stop +Rethinking Classical Concurrency Patterns: slides, video +When Go programs end +Documentation Conventions: Contexts + +Interfaces + +Avoid creating interfaces until a real need exists. Focus on the required behavior rather than just abstract named patterns like “service” or “repository” and the like. + +Do not wrap RPC clients in new manual interfaces just for the sake of abstraction or testing. Use real transports instead (testing RPC). + +Do not define back doors or export test double implementations of an interface solely for testing. Prefer testing via the public API of the real implementation instead. + +Design interfaces to be small for easier implementation and composition (GoTip #78: Minimal Viable Interfaces). Document interfaces appropriately including their contract, edge cases, and expected errors. Keep interface types unexported if they are only used internally within a package. + +The consumer of the interface should define it (not the package implementing the interface), ensuring it includes only the methods they actually use. The producer package may export the interface if the interface is the product (a common protocol) to prevent interface redefinition bloat. + +There is an adage: Functions should take interfaces as arguments but return concrete types (GoTip #49: Accept Interfaces, Return Concrete Types). Returning concrete types allows the caller to have access to every public method and field of that specific implementation, not just the subset of methods defined in a pre-chosen interface. The caller can still pass that concrete result into any other function that expects an interface. Sometimes returning an interface is acceptable for encapsulation (e.g., error interface), and certain constructs like command, chaining, factory, and strategy patterns. + +Deeper discussion on interfaces exists in the Best Practices’ section on interfaces. + +Generics + +Generics (formally called “Type Parameters”) are allowed where they fulfill your business requirements. In many applications, a conventional approach using existing language features (slices, maps, interfaces, and so on) works just as well without the added complexity, so be wary of premature use. See the discussion on least mechanism. + +When introducing an exported API that uses generics, make sure it is suitably documented. It’s highly encouraged to include motivating runnable examples. + +Do not use generics just because you are implementing an algorithm or data structure that does not care about the type of its member elements. If there is only one type being instantiated in practice, start by making your code work on that type without using generics at all. Adding polymorphism later will be straightforward compared to removing abstraction that is found to be unnecessary. + +Do not use generics to invent domain-specific languages (DSLs). In particular, refrain from introducing error-handling frameworks that might put a significant burden on readers. Instead prefer established error handling practices. For testing, be especially wary of introducing assertion libraries or frameworks that result in less useful test failures. + +In general: + +Write code, don’t design types. From a GopherCon talk by Robert Griesemer and Ian Lance Taylor. +If you have several types that share a useful unifying interface, consider modeling the solution using that interface. Generics may not be needed. +Otherwise, instead of relying on the any type and excessive type switching, consider generics. + +See also: + +Using Generics in Go, talk by Ian Lance Taylor + +Generics tutorial on Go’s webpage + +Pass values + +Do not pass pointers as function arguments just to save a few bytes. If a function reads its argument x only as *x throughout, then the argument shouldn’t be a pointer. Common instances of this include passing a pointer to a string (*string) or a pointer to an interface value (*io.Reader). In both cases, the value itself is a fixed size and can be passed directly. + +This advice does not apply to large structs, or even small structs that may increase in size. In particular, protocol buffer messages should generally be handled by pointer rather than by value. The pointer type satisfies the proto.Message interface (accepted by proto.Marshal, protocmp.Transform, etc.), and protocol buffer messages can be quite large and often grow larger over time. + +Receiver type + +A method receiver can be passed either as a value or a pointer, just as if it were a regular function parameter. The choice between the two is based on which method set(s) the method should be a part of. + +Correctness wins over speed or simplicity. There are cases where you must use a pointer value. In other cases, pick pointers for large types or as future-proofing if you don’t have a good sense of how the code will grow, and use values for simple plain old data. + +The list below spells out each case in further detail: + +If the receiver is a slice and the method doesn’t reslice or reallocate the slice, use a value rather than a pointer. + +// Good: +type Buffer []byte + +func (b Buffer) Len() int { return len(b) } + + +If the method needs to mutate the receiver, the receiver must be a pointer. + +// Good: +type Counter int + +func (c *Counter) Inc() { *c++ } + +// See https://pkg.go.dev/container/heap. +type Queue []Item + +func (q *Queue) Push(x Item) { *q = append([]Item{x}, *q...) } + + +If the receiver is a struct containing fields that cannot safely be copied, use a pointer receiver. Common examples are sync.Mutex and other synchronization types. + +// Good: +type Counter struct { + mu sync.Mutex + total int +} + +func (c *Counter) Inc() { + c.mu.Lock() + defer c.mu.Unlock() + c.total++ +} + + +Tip: Check the type’s Godoc for information about whether it is safe or unsafe to copy. + +If the receiver is a “large” struct or array, a pointer receiver may be more efficient. Passing a struct is equivalent to passing all of its fields or elements as arguments to the method. If that seems too large to pass by value, a pointer is a good choice. + +For methods that will call or run concurrently with other functions that modify the receiver, use a value if those modifications should not be visible to your method; otherwise use a pointer. + +If the receiver is a struct or array, any of whose elements is a pointer to something that may be mutated, prefer a pointer receiver to make the intention of mutability clear to the reader. + +// Good: +type Counter struct { + m *Metric +} + +func (c *Counter) Inc() { + c.m.Add(1) +} + + +If the receiver is a built-in type, such as an integer or a string, that does not need to be modified, use a value. + +// Good: +type User string + +func (u User) String() { return string(u) } + + +If the receiver is a map, function, or channel, use a value rather than a pointer. + +// Good: +// See https://pkg.go.dev/net/http#Header. +type Header map[string][]string + +func (h Header) Add(key, value string) { /* omitted */ } + + +If the receiver is a “small” array or struct that is naturally a value type with no mutable fields and no pointers, a value receiver is usually the right choice. + +// Good: +// See https://pkg.go.dev/time#Time. +type Time struct { /* omitted */ } + +func (t Time) Add(d Duration) Time { /* omitted */ } + + +When in doubt, use a pointer receiver. + +As a general guideline, prefer to make the methods for a type either all pointer methods or all value methods. + +Note: There is a lot of misinformation about whether passing a value or a pointer to a function can affect performance. The compiler can choose to pass pointers to values on the stack as well as copying values on the stack, but these considerations should not outweigh the readability and correctness of the code in most circumstances. When the performance does matter, it is important to profile both approaches with a realistic benchmark before deciding that one approach outperforms the other. + +switch and break + +Do not use break statements without target labels at the ends of switch clauses; they are redundant. Unlike in C and Java, switch clauses in Go automatically break, and a fallthrough statement is needed to achieve the C-style behavior. Use a comment rather than break if you want to clarify the purpose of an empty clause. + +// Good: +switch x { +case "A", "B": + buf.WriteString(x) +case "C": + // handled outside of the switch statement +default: + return fmt.Errorf("unknown value: %q", x) +} + +// Bad: +switch x { +case "A", "B": + buf.WriteString(x) + break // this break is redundant +case "C": + break // this break is redundant +default: + return fmt.Errorf("unknown value: %q", x) +} + + +Note: If a switch clause is within a for loop, using break within switch does not exit the enclosing for loop. + +for { + switch x { + case "A": + break // exits the switch, not the loop + } +} + + +To escape the enclosing loop, use a label on the for statement: + +loop: + for { + switch x { + case "A": + break loop // exits the loop + } + } + + +Synchronous functions + +Synchronous functions return their results directly and finish any callbacks or channel operations before returning. Prefer synchronous functions over asynchronous functions. + +Synchronous functions keep goroutines localized within a call. This helps to reason about their lifetimes, and avoid leaks and data races. Synchronous functions are also easier to test, since the caller can pass an input and check the output without the need for polling or synchronization. + +If necessary, the caller can add concurrency by calling the function in a separate goroutine. However, it is quite difficult (sometimes impossible) to remove unnecessary concurrency at the caller side. + +See also: + +“Rethinking Classical Concurrency Patterns”, talk by Bryan Mills: slides, video + +Type aliases + +Use a type definition, type T1 T2, to define a new type. Use a type alias, type T1 = T2, to refer to an existing type without defining a new type. Type aliases are rare; their primary use is to aid migrating packages to new source code locations. Don’t use type aliasing when it is not needed. + +Use %q + +Go’s format functions (fmt.Printf etc.) have a %q verb which prints strings inside double-quotation marks. + +// Good: +fmt.Printf("value %q looks like English text", someText) + + +Prefer using %q over doing the equivalent manually, using %s: + +// Bad: +fmt.Printf("value \"%s\" looks like English text", someText) +// Avoid manually wrapping strings with single-quotes too: +fmt.Printf("value '%s' looks like English text", someText) + + +Using %q is recommended in output intended for humans where the input value could possibly be empty or contain control characters. It can be very hard to notice a silent empty string, but "" stands out clearly as such. + +Use any + +Go 1.18 introduces an any type as an alias to interface{}. Because it is an alias, any is equivalent to interface{} in many situations and in others it is easily interchangeable via an explicit conversion. Prefer to use any in new code. + +Common libraries + +Flags + +Go programs in the Google codebase use an internal variant of the standard flag package. It has a similar interface but interoperates well with internal Google systems. Flag names in Go binaries should prefer to use underscores to separate words, though the variables that hold a flag’s value should follow the standard Go name style (mixed caps). Specifically, the flag name should be in snake case, and the variable name should be the equivalent name in camel case. + +// Good: +var ( + pollInterval = flag.Duration("poll_interval", time.Minute, "Interval to use for polling.") +) + +// Bad: +var ( + poll_interval = flag.Int("pollIntervalSeconds", 60, "Interval to use for polling in seconds.") +) + + +Flags must only be defined in package main or equivalent. + +General-purpose packages should be configured using Go APIs, not by punching through to the command-line interface; don’t let importing a library export new flags as a side effect. That is, prefer explicit function arguments or struct field assignment or much less frequently and under the strictest of scrutiny exported global variables. In the extremely rare case that it is necessary to break this rule, the flag name must clearly indicate the package that it configures. + +If your flags are global variables, place them in their own var group, following the imports section. + +There is additional discussion around best practices for creating complex CLIs with subcommands. + +See also: + +Tip of the Week #45: Avoid Flags, Especially in Library Code +Go Tip #10: Configuration Structs and Flags +Go Tip #80: Dependency Injection Principles + +Logging + +Go programs in the Google codebase use a variant of the standard log package. It has a similar but more powerful interface and interoperates well with internal Google systems. An open source version of this library is available as package glog, and open source Google projects may use that, but this guide refers to it as log throughout. + +Note: For abnormal program exits, this library uses log.Fatal to abort with a stacktrace, and log.Exit to stop without one. There is no log.Panic function as in the standard library. + +Tip: log.Info(v) is equivalent log.Infof("%v", v), and the same goes for other logging levels. Prefer the non-formatting version when you have no formatting to do. + +See also: + +Best practices on logging errors and custom verbosity levels +When and how to use the log package to stop the program + +Contexts + +Values of the context.Context type carry security credentials, tracing information, deadlines, and cancellation signals across API and process boundaries. Unlike C++ and Java, which in the Google codebase use thread-local storage, Go programs pass contexts explicitly along the entire function call chain from incoming RPCs and HTTP requests to outgoing requests. + +When passed to a function or method, context.Context is always the first parameter. + +func F(ctx context.Context /* other arguments */) {} + + +Exceptions are: + +In an HTTP handler, where the context comes from req.Context(). + +In streaming RPC methods, where the context comes from the stream. + +Code using gRPC streaming accesses a context from a Context() method in the generated server type, which implements grpc.ServerStream. See gRPC Generated Code documentation. + +In test functions (e.g. TestXXX, BenchmarkXXX, FuzzXXX), where the context comes from (testing.TB).Context(). + +In other entrypoint functions (see below for examples of such functions), use context.Background(). + +In binary targets: main +In general purpose code and libraries: init + +Note: It is very rare for code in the middle of a callchain to require creating a base context of its own using context.Background(). Always prefer taking a context from your caller, unless it’s the wrong context. + +You may come across server libraries (the implementation of Stubby, gRPC, or HTTP in Google’s server framework for Go) that construct a fresh context object per request. These contexts are immediately filled with information from the incoming request, so that when passed to the request handler, the context’s attached values have been propagated to it across the network boundary from the client caller. Moreover, these contexts’ lifetimes are scoped to that of the request: when the request is finished, the context is cancelled. + +Unless you are implementing a server framework, you shouldn’t create contexts with context.Background() in library code. Instead, prefer using context detachment, which is mentioned below, if there is an existing context available. If you think you do need context.Background() outside of entrypoint functions, discuss it with the Google Go style mailing list before committing to an implementation. + +The convention that context.Context comes first in functions also applies to test helpers. + +// Good: +func readTestFile(ctx context.Context, t *testing.T, path string) string {} + + +Do not add a context member to a struct type. Instead, add a context parameter to each method on the type that needs to pass it along. The one exception is for methods whose signature must match an interface in the standard library or in a third party library outside Google’s control. Such cases are very rare, and should be discussed with the Google Go style mailing list before implementation and readability review. + +Note: Go 1.24 added a (testing.TB).Context() method. In tests, prefer using (testing.TB).Context() over context.Background() to provide the initial context.Context used by the test. Helper functions, environment or test double setup, and other functions called from the test function body that require a context should have one explicitly passed. + +Code in the Google codebase that must spawn background operations which can run after the parent context has been cancelled can use an internal package for detachment. Follow issue #40221 for discussions on an open source alternative. + +Since contexts are immutable, it is fine to pass the same context to multiple calls that share the same deadline, cancellation signal, credentials, parent trace, and so on. + +See also: + +Contexts and structs + +Custom contexts + +Do not create custom context types or use interfaces other than context.Context in function signatures. There are no exceptions to this rule. + +Imagine if every team had a custom context. Every function call from package p to package q would have to determine how to convert a p.Context to a q.Context, for all pairs of packages p and q. This is impractical and error-prone for humans, and it makes automated refactorings that add context parameters nearly impossible. + +If you have application data to pass around, put it in a parameter, in the receiver, in globals, or in a Context value if it truly belongs there. Creating your own context type is not acceptable since it undermines the ability of the Go team to make Go programs work properly in production. + +crypto/rand + +Do not use package math/rand to generate keys, even throwaway ones. If unseeded, the generator is completely predictable. Seeded with time.Nanoseconds(), there are just a few bits of entropy. Instead, use crypto/rand’s Reader, and if you need text, print to hexadecimal or base64. + +// Good: +import ( + "crypto/rand" + // "encoding/base64" + // "encoding/hex" + "fmt" + + // ... +) + +func Key() string { + buf := make([]byte, 16) + if _, err := rand.Read(buf); err != nil { + log.Fatalf("Out of randomness, should never happen: %v", err) + } + return fmt.Sprintf("%x", buf) + // or hex.EncodeToString(buf) + // or base64.StdEncoding.EncodeToString(buf) +} + + +Note: log.Fatalf is not the standard library log. See [#logging]. + +Useful test failures + +It should be possible to diagnose a test’s failure without reading the test’s source. Tests should fail with helpful messages detailing: + +What caused the failure +What inputs resulted in an error +The actual result +What was expected + +Specific conventions for achieving this goal are outlined below. + +Assertion libraries + +Do not create “assertion libraries” as helpers for testing. + +Assertion libraries are libraries that attempt to combine the validation and production of failure messages within a test (though the same pitfalls can apply to other test helpers as well). For more on the distinction between test helpers and assertion libraries, see best practices. + +// Bad: +var obj BlogPost + +assert.IsNotNil(t, "obj", obj) +assert.StringEq(t, "obj.Type", obj.Type, "blogPost") +assert.IntEq(t, "obj.Comments", obj.Comments, 2) +assert.StringNotEq(t, "obj.Body", obj.Body, "") + + +Assertion libraries tend to either stop the test early (if assert calls t.Fatalf or panic) or omit relevant information about what the test got right: + +// Bad: +package assert + +func IsNotNil(t *testing.T, name string, val any) { + if val == nil { + t.Fatalf("Data %s = nil, want not nil", name) + } +} + +func StringEq(t *testing.T, name, got, want string) { + if got != want { + t.Fatalf("Data %s = %q, want %q", name, got, want) + } +} + + +Complex assertion functions often do not provide useful failure messages and context that exists within the test function. Too many assertion functions and libraries lead to a fragmented developer experience: which assertion library should I use, what style of output format should it emit, etc.? Fragmentation produces unnecessary confusion, especially for library maintainers and authors of large-scale changes, who are responsible for fixing potential downstream breakages. Instead of creating a domain-specific language for testing, use Go itself. + +Assertion libraries often factor out comparisons and equality checks. Prefer using standard libraries such as cmp and fmt instead: + +// Good: +var got BlogPost + +want := BlogPost{ + Comments: 2, + Body: "Hello, world!", +} + +if !cmp.Equal(got, want) { + t.Errorf("Blog post = %v, want = %v", got, want) +} + + +For more domain-specific comparison helpers, prefer returning a value or an error that can be used in the test’s failure message instead of passing *testing.T and calling its error reporting methods: + +// Good: +func postLength(p BlogPost) int { return len(p.Body) } + +func TestBlogPost_VeritableRant(t *testing.T) { + post := BlogPost{Body: "I am Gunnery Sergeant Hartman, your senior drill instructor."} + + if got, want := postLength(post), 60; got != want { + t.Errorf("Length of post = %v, want %v", got, want) + } +} + + +Best Practice: Were postLength non-trivial, it would make sense to test it directly, independently of any tests that use it. + +See also: + +Equality comparison and diffs +Print diffs +For more on the distinction between test helpers and assertion helpers, see best practices +Go FAQ section on testing frameworks and their opinionated absence + +Identify the function + +In most tests, failure messages should include the name of the function that failed, even though it seems obvious from the name of the test function. Specifically, your failure message should be YourFunc(%v) = %v, want %v instead of just got %v, want %v. + +Identify the input + +In most tests, failure messages should include the function inputs if they are short. If the relevant properties of the inputs are not obvious (for example, because the inputs are large or opaque), you should name your test cases with a description of what’s being tested and print the description as part of your error message. + +Got before want + +Test outputs should include the actual value that the function returned before printing the value that was expected. A standard format for printing test outputs is YourFunc(%v) = %v, want %v. Where you would write “actual” and “expected”, prefer using the words “got” and “want”, respectively. + +For diffs, directionality is less apparent, and as such it is important to include a key to aid in interpreting the failure. See the section on printing diffs. Whichever diff order you use in your failure messages, you should explicitly indicate it as a part of the failure message, because existing code is inconsistent about the ordering. + +Full structure comparisons + +If your function returns a struct (or any data type with multiple fields such as slices, arrays, and maps), avoid writing test code that performs a hand-coded field-by-field comparison of the struct. Instead, construct the data that you’re expecting your function to return, and compare directly using a deep comparison. + +Note: This does not apply if your data contains irrelevant fields that obscure the intention of the test. + +If your struct needs to be compared for approximate (or equivalent kind of semantic) equality or it contains fields that cannot be compared for equality (e.g., if one of the fields is an io.Reader), tweaking a cmp.Diff or cmp.Equal comparison with cmpopts options such as cmpopts.IgnoreInterfaces may meet your needs (example). + +If your function returns multiple return values, you don’t need to wrap those in a struct before comparing them. Just compare the return values individually and print them. + +// Good: +val, multi, tail, err := strconv.UnquoteChar(`\"Fran & Freddie's Diner\"`, '"') +if err != nil { + t.Fatalf(...) +} +if val != `"` { + t.Errorf(...) +} +if multi { + t.Errorf(...) +} +if tail != `Fran & Freddie's Diner"` { + t.Errorf(...) +} + + +Compare stable results + +Avoid comparing results that may depend on output stability of a package that you do not own. Instead, the test should compare on semantically relevant information that is stable and resistant to changes in dependencies. For functionality that returns a formatted string or serialized bytes, it is generally not safe to assume that the output is stable. + +For example, json.Marshal can change (and has changed in the past) the specific bytes that it emits. Tests that perform string equality on the JSON string may break if the json package changes how it serializes the bytes. Instead, a more robust test would parse the contents of the JSON string and ensure that it is semantically equivalent to some expected data structure. + +Keep going + +Tests should keep going for as long as possible, even after a failure, in order to print out all of the failed checks in a single run. This way, a developer who is fixing the failing test doesn’t have to re-run the test after fixing each bug to find the next bug. + +Prefer calling t.Error over t.Fatal for reporting a mismatch. When comparing several different properties of a function’s output, use t.Error for each of those comparisons. + +// Good: +gotMean, gotVariance, err := MyDistribution(input) +if err != nil { + t.Fatalf("MyDistribution(%v) returned unexpected error: %v", input, err) +} +if diff := cmp.Diff(wantMean, gotMean); diff != "" { + t.Errorf("MyDistribution(%v) returned unexpected difference in mean value (-want +got):\n%s", input, diff) +} +if diff := cmp.Diff(wantVariance, gotVariance); diff != "" { + t.Errorf("MyDistribution(%v) returned unexpected difference in variance value (-want +got):\n%s", input, diff) +} + + +Calling t.Fatal is primarily useful for reporting an unexpected condition (such as an error or output mismatch) when subsequent failures would be meaningless or even mislead the investigator. Note how the code below calls t.Fatalf and then t.Errorf: + +// Good: +gotEncoded := Encode(input) +if gotEncoded != wantEncoded { + t.Fatalf("Encode(%q) = %q, want %q", input, gotEncoded, wantEncoded) + // It doesn't make sense to decode from unexpected encoded input. +} +gotDecoded, err := Decode(gotEncoded) +if err != nil { + t.Fatalf("Decode(%q) returned unexpected error: %v", gotEncoded, err) +} +if gotDecoded != input { + t.Errorf("Decode(%q) = %q, want %q", gotEncoded, gotDecoded, input) +} + + +For table-driven test, consider using subtests and use t.Fatal rather than t.Error and continue. See also GoTip #25: Subtests: Making Your Tests Lean. + +Best practice: For more discussion about when t.Fatal should be used, see best practices. + +Equality comparison and diffs + +The == operator evaluates equality using language-defined comparisons. Scalar values (numbers, booleans, etc) are compared based on their values, but only some structs and interfaces can be compared in this way. Pointers are compared based on whether they point to the same variable, rather than based on the equality of the values to which they point. + +The cmp package can compare more complex data structures not appropriately handled by ==, such as slices. Use cmp.Equal for equality comparison and cmp.Diff to obtain a human-readable diff between objects. + +// Good: +want := &Doc{ + Type: "blogPost", + Comments: 2, + Body: "This is the post body.", + Authors: []string{"isaac", "albert", "emmy"}, +} +if !cmp.Equal(got, want) { + t.Errorf("AddPost() = %+v, want %+v", got, want) +} + + +As a general-purpose comparison library, cmp may not know how to compare certain types. For example, it can only compare protocol buffer messages if passed the protocmp.Transform option. + +// Good: +if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { + t.Errorf("Foo() returned unexpected difference in protobuf messages (-want +got):\n%s", diff) +} + + +Although the cmp package is not part of the Go standard library, it is maintained by the Go team and should produce stable equality results over time. It is user-configurable and should serve most comparison needs. + +Existing code may make use of the following older libraries, and may continue using them for consistency: + +pretty produces aesthetically pleasing difference reports. However, it quite deliberately considers values that have the same visual representation as equal. In particular, pretty does not catch differences between nil slices and empty ones, is not sensitive to different interface implementations with identical fields, and it is possible to use a nested map as the basis for comparison with a struct value. It also serializes the entire value into a string before producing a diff, and as such is not a good choice for comparing large values. By default, it compares unexported fields, which makes it sensitive to changes in implementation details in your dependencies. For this reason, it is not appropriate to use pretty on protobuf messages. + +Prefer using cmp for new code, and it is worth considering updating older code to use cmp where and when it is practical to do so. + +Older code may use the standard library reflect.DeepEqual function to compare complex structures. reflect.DeepEqual should not be used for checking equality, as it is sensitive to changes in unexported fields and other implementation details. Code that is using reflect.DeepEqual should be updated to one of the above libraries. + +Note: The cmp package is designed for testing, rather than production use. As such, it may panic when it suspects that a comparison is performed incorrectly to provide instruction to users on how to improve the test to be less brittle. Given cmp’s propensity towards panicking, it makes it unsuitable for code that is used in production as a spurious panic may be fatal. + +Level of detail + +The conventional failure message, which is suitable for most Go tests, is YourFunc(%v) = %v, want %v. However, there are cases that may call for more or less detail: + +Tests performing complex interactions should describe the interactions too. For example, if the same YourFunc is called several times, identify which call failed the test. If it’s important to know any extra state of the system, include that in the failure output (or at least in the logs). +If the data is a complex struct with significant boilerplate, it is acceptable to describe only the important parts in the message, but do not overly obscure the data. +Setup failures do not require the same level of detail. If a test helper populates a Spanner table but Spanner was down, you probably don’t need to include which test input you were going to store in the database. t.Fatalf("Setup: Failed to set up test database: %s", err) is usually helpful enough to resolve the issue. + +Tip: Make your failure mode trigger during development. Review what the failure message looks like and whether a maintainer can effectively deal with the failure. + +There are some techniques for reproducing test inputs and outputs clearly: + +When printing string data, %q is often useful to emphasize that the value is important and to more easily spot bad values. +When printing (small) structs, %+v can be more useful than %v. +When validation of larger values fails, printing a diff can make it easier to understand the failure. + +Print diffs + +If your function returns large output then it can be hard for someone reading the failure message to find the differences when your test fails. Instead of printing both the returned value and the wanted value, make a diff. + +To compute diffs for such values, cmp.Diff is preferred, particularly for new tests and new code, but other tools may be used. See types of equality for guidance regarding the strengths and weaknesses of each function. + +cmp.Diff + +pretty.Compare + +You can use the diff package to compare multi-line strings or lists of strings. You can use this as a building block for other kinds of diffs. + +Add some text to your failure message explaining the direction of the diff. + +Something like diff (-want +got) is good when you’re using the cmp, pretty, and diff packages (if you pass (want, got) to the function), because the - and + that you add to your format string will match the - and + that actually appear at the beginning of the diff lines. If you pass (got, want) to your function, the correct key would be (-got +want) instead. + +The messagediff package uses a different output format, so the message diff (want -> got) is appropriate when you’re using it (if you pass (want, got) to the function), because the direction of the arrow will match the direction of the arrow in the “modified” lines. + +The diff will span multiple lines, so you should print a newline before you print the diff. + +Test error semantics + +When a unit test performs string comparisons or uses a vanilla cmp to check that particular kinds of errors are returned for particular inputs, you may find that your tests are brittle if any of those error messages are reworded in the future. Since this has the potential to turn your unit test into a change detector (see TotT: Change-Detector Tests Considered Harmful ), don’t use string comparison to check what type of error your function returns. However, it is permissible to use string comparisons to check that error messages coming from the package under test satisfy certain properties, for example, that it includes the parameter name. + +Error values in Go typically have a component intended for human eyes and a component intended for semantic control flow. Tests should seek to only test semantic information that can be reliably observed, rather than display information that is intended for human debugging, as this is often subject to future changes. For guidance on constructing errors with semantic meaning see best-practices regarding errors. If an error with insufficient semantic information is coming from a dependency outside your control, consider filing a bug against the owner to help improve the API, rather than relying on parsing the error message. + +Within unit tests, it is common to only care whether an error occurred or not. If so, then it is sufficient to only test whether the error was non-nil when you expected an error. If you would like to test that the error semantically matches some other error, then consider using errors.Is or cmp with cmpopts.EquateErrors. + +Note: If a test uses cmpopts.EquateErrors but all of its wantErr values are either nil or cmpopts.AnyError, then using cmp is unnecessary mechanism. Simplify the code by making the want field a bool. You can then use a simple comparison with !=. + +// Good: +err := f(test.input) +if gotErr := err != nil; gotErr != test.wantErr { + t.Errorf("f(%q) = %v, want error presence = %v", test.input, err, test.wantErr) +} + + +See also GoTip #13: Designing Errors for Checking. + +Test structure + +Subtests + +The standard Go testing library offers a facility to define subtests. This allows flexibility in setup and cleanup, controlling parallelism, and test filtering. Subtests can be useful (particularly for table-driven tests), but using them is not mandatory. See also the Go blog post about subtests. + +Subtests should not depend on the execution of other cases for success or initial state, because subtests are expected to be able to be run individually with using go test -run flags or with Bazel test filter expressions. + +Subtest names + +Name your subtest such that it is readable in test output and useful on the command line for users of test filtering. When you use t.Run to create a subtest, the first argument is used as a descriptive name for the test. To ensure that test results are legible to humans reading the logs, choose subtest names that will remain useful and readable after escaping. Think of subtest names more like a function identifier than a prose description. + +The test runner replaces spaces with underscores, and escapes non-printing characters. To ensure accurate correlation between test logs and source code, it is recommended to avoid using these characters in subtest names. + +If your test data benefits from a longer description, consider putting the description in a separate field (perhaps to be printed using t.Log or alongside failure messages). + +Subtests may be run individually using flags to the Go test runner or Bazel test filter, so choose descriptive names that are also easy to type. + +Warning: Slash characters are particularly unfriendly in subtest names, since they have special meaning for test filters. + +# Bad: +# Assuming TestTime and t.Run("America/New_York", ...) +bazel test :mytest --test_filter="Time/New_York" # Runs nothing! +bazel test :mytest --test_filter="Time//New_York" # Correct, but awkward. + + +To identify the inputs of the function, include them in the test’s failure messages, where they won’t be escaped by the test runner. + +// Good: +func TestTranslate(t *testing.T) { + data := []struct { + name, desc, srcLang, dstLang, srcText, wantDstText string + }{ + { + name: "hu=en_bug-1234", + desc: "regression test following bug 1234. contact: cleese", + srcLang: "hu", + srcText: "cigarettát és egy öngyújtót kérek", + dstLang: "en", + wantDstText: "cigarettes and a lighter please", + }, // ... + } + for _, d := range data { + t.Run(d.name, func(t *testing.T) { + got := Translate(d.srcLang, d.dstLang, d.srcText) + if got != d.wantDstText { + t.Errorf("%s\nTranslate(%q, %q, %q) = %q, want %q", + d.desc, d.srcLang, d.dstLang, d.srcText, got, d.wantDstText) + } + }) + } +} + + +Here are a few examples of things to avoid: + +// Bad: +// Too wordy. +t.Run("check that there is no mention of scratched records or hovercrafts", ...) +// Slashes cause problems on the command line. +t.Run("AM/PM confusion", ...) + + +See also Go Tip #117: Subtest Names. + +Table-driven tests + +Use table-driven tests when many different test cases can be tested using similar testing logic. + +When testing whether the actual output of a function is equal to the expected output. For example, the many tests of fmt.Sprintf or the minimal snippet below. +When testing whether the outputs of a function always conform to the same set of invariants. For example, tests for net.Dial. + +Here is the minimal structure of a table-driven test. If needed, you may use different names or add extra facilities such as subtests or setup and cleanup functions. Always keep useful test failures in mind. + +// Good: +func TestCompare(t *testing.T) { + compareTests := []struct { + a, b string + want int + }{ + {"", "", 0}, + {"a", "", 1}, + {"", "a", -1}, + {"abc", "abc", 0}, + {"ab", "abc", -1}, + {"abc", "ab", 1}, + {"x", "ab", 1}, + {"ab", "x", -1}, + {"x", "a", 1}, + {"b", "x", -1}, + // test runtime·memeq's chunked implementation + {"abcdefgh", "abcdefgh", 0}, + {"abcdefghi", "abcdefghi", 0}, + {"abcdefghi", "abcdefghj", -1}, + } + + for _, test := range compareTests { + got := Compare(test.a, test.b) + if got != test.want { + t.Errorf("Compare(%q, %q) = %v, want %v", test.a, test.b, got, test.want) + } + } +} + + +Note: The failure messages in this example above fulfill the guidance to identify the function and identify the input. There’s no need to identify the row numerically. + +When some test cases need to be checked using different logic from other test cases, it is appropriate to write multiple test functions, as explained in GoTip #50: Disjoint Table Tests. + +When the additional test cases are simple (e.g., basic error checking) and don’t introduce conditionalized code flow in the table test’s loop body, it’s permissible to include that case in the existing test, though be careful using logic like this. What starts simple today can organically grow into something unmaintainable. + +For example: + +func TestDivide(t *testing.T) { + tests := []struct { + dividend, divisor int + want int + wantErr bool + }{ + { + dividend: 4, + divisor: 2, + want: 2, + }, + { + dividend: 10, + divisor: 2, + want: 5, + }, + { + dividend: 1, + divisor: 0, + wantErr: true, + }, + } + + for _, test := range tests { + got, err := Divide(test.dividend, test.divisor) + if (err != nil) != test.wantErr { + t.Errorf("Divide(%d, %d) error = %v, want error presence = %t", test.dividend, test.divisor, err, test.wantErr) + } + + // In this example, we're only testing the value result when the tested function didn't fail. + if err != nil { + continue + } + + if got != test.want { + t.Errorf("Divide(%d, %d) = %d, want %d", test.dividend, test.divisor, got, test.want) + } + } +} + + +More complicated logic in your test code, like complex error checking based on conditional differences in test setup (often based on table test input parameters), can be difficult to understand when each entry in a table has specialized logic based on the inputs. If test cases have different logic but identical setup, a sequence of subtests within a single test function might be more readable. A test helper may also be useful for simplifying test setup in order to maintain the readability of a test body. + +You can combine table-driven tests with multiple test functions. For example, when testing that a function’s output exactly matches the expected output and that the function returns a non-nil error for an invalid input, then writing two separate table-driven test functions is the best approach: one for normal non-error outputs, and one for error outputs. + +Data-driven test cases + +Table test rows can sometimes become complicated, with the row values dictating conditional behavior inside the test case. The extra clarity from the duplication between the test cases is necessary for readability. + +// Good: +type decodeCase struct { + name string + input string + output string + err error +} + +func TestDecode(t *testing.T) { + // setupCodex is slow as it creates a real Codex for the test. + codex := setupCodex(t) + + var tests []decodeCase // rows omitted for brevity + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + output, err := Decode(test.input, codex) + if got, want := output, test.output; got != want { + t.Errorf("Decode(%q) = %v, want %v", test.input, got, want) + } + if got, want := err, test.err; !cmp.Equal(got, want) { + t.Errorf("Decode(%q) err %q, want %q", test.input, got, want) + } + }) + } +} + +func TestDecodeWithFake(t *testing.T) { + // A fakeCodex is a fast approximation of a real Codex. + codex := newFakeCodex() + + var tests []decodeCase // rows omitted for brevity + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + output, err := Decode(test.input, codex) + if got, want := output, test.output; got != want { + t.Errorf("Decode(%q) = %v, want %v", test.input, got, want) + } + if got, want := err, test.err; !cmp.Equal(got, want) { + t.Errorf("Decode(%q) err %q, want %q", test.input, got, want) + } + }) + } +} + + +In the counterexample below, note how hard it is to distinguish between which type of Codex is used per test case in the case setup. (The highlighted parts run afoul of the advice from TotT: Data Driven Traps! .) + +// Bad: +type decodeCase struct { + name string + input string + codex testCodex + output string + err error +} + +type testCodex int + +const ( + fake testCodex = iota + prod +) + +func TestDecode(t *testing.T) { + var tests []decodeCase // rows omitted for brevity + + for _, test := tests { + t.Run(test.name, func(t *testing.T) { + var codex Codex + switch test.codex { + case fake: + codex = newFakeCodex() + case prod: + codex = setupCodex(t) + default: + t.Fatalf("Unknown codex type: %v", codex) + } + output, err := Decode(test.input, codex) + if got, want := output, test.output; got != want { + t.Errorf("Decode(%q) = %q, want %q", test.input, got, want) + } + if got, want := err, test.err; !cmp.Equal(got, want) { + t.Errorf("Decode(%q) err %q, want %q", test.input, got, want) + } + }) + } +} + + +Identifying the row + +Do not use the index of the test in the test table as a substitute for naming your tests or printing the inputs. Nobody wants to go through your test table and count the entries in order to figure out which test case is failing. + +// Bad: +tests := []struct { + input, want string +}{ + {"hello", "HELLO"}, + {"wORld", "WORLD"}, +} +for i, d := range tests { + if strings.ToUpper(d.input) != d.want { + t.Errorf("Failed on case #%d", i) + } +} + + +Add a test description to your test struct and print it along failure messages. When using subtests, your subtest name should be effective in identifying the row. + +Important: Even though t.Run scopes the output and execution, you must always identify the input. The table test row names must follow the subtest naming guidance. + +Test helpers + +A test helper is a function that performs a setup or cleanup task. All failures that occur in test helpers are expected to be failures of the environment (not from the code under test) — for example when a test database cannot be started because there are no more free ports on this machine. + +If you pass a *testing.T, call t.Helper to attribute failures in the test helper to the line where the helper is called. This parameter should come after a context parameter, if present, and before any remaining parameters. + +// Good: +func TestSomeFunction(t *testing.T) { + golden := readFile(t, "testdata/golden-result.txt") + // ... tests against golden ... +} + +// readFile returns the contents of a data file. +// It must only be called from the same goroutine as started the test. +func readFile(t *testing.T, filename string) string { + t.Helper() + contents, err := runfiles.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + return string(contents) +} + + +Do not use this pattern when it obscures the connection between a test failure and the conditions that led to it. Specifically, the guidance about assert libraries still applies, and t.Helper should not be used to implement such libraries. + +Tip: For more on the distinction between test helpers and assertion helpers, see best practices. + +Although the above refers to *testing.T, much of the advice stays the same for benchmark and fuzz helpers. + +Test package + +Tests in the same package + +Tests may be defined in the same package as the code being tested. + +To write a test in the same package: + +Place the tests in a foo_test.go file +Use package foo for the test file +Do not explicitly import the package to be tested +# Good: +go_library( + name = "foo", + srcs = ["foo.go"], + deps = [ + ... + ], +) + +go_test( + name = "foo_test", + size = "small", + srcs = ["foo_test.go"], + library = ":foo", + deps = [ + ... + ], +) + + +A test in the same package can access unexported identifiers in the package. This may enable better test coverage and more concise tests. Be aware that any examples declared in the test will not have the package names that a user will need in their code. + +Tests in a different package + +It is not always appropriate or even possible to define a test in the same package as the code being tested. In these cases, use a package name with the _test suffix. This is an exception to the “no underscores” rule to package names. For example: + +If an integration test does not have an obvious library that it belongs to + +// Good: +package gmailintegration_test + +import "testing" + + +If defining the tests in the same package results in circular dependencies + +// Good: +package fireworks_test + +import ( + "fireworks" + "fireworkstestutil" // fireworkstestutil also imports fireworks +) + + +Use package testing + +The Go standard library provides the testing package. This is the only testing framework permitted for Go code in the Google codebase. In particular, assertion libraries and third-party testing frameworks are not allowed. + +The testing package provides a minimal but complete set of functionality for writing good tests: + +Top-level tests +Benchmarks +Runnable examples +Subtests +Logging +Failures and fatal failures + +These are designed to work cohesively with core language features like composite literal and if-with-initializer syntax to enable test authors to write [clear, readable, and maintainable tests]. + +Non-decisions + +A style guide cannot enumerate positive prescriptions for all matters, nor can it enumerate all matters about which it does not offer an opinion. That said, here are a few things where the readability community has previously debated and has not achieved consensus about. + +Local variable initialization with zero value. var i int and i := 0 are equivalent. See also initialization best practices. +Empty composite literal vs. new or make. &File{} and new(File) are equivalent. So are map[string]bool{} and make(map[string]bool). See also composite declaration best practices. +got, want argument ordering in cmp.Diff calls. Be locally consistent, and include a legend in your failure message. +errors.New vs fmt.Errorf on non-formatted strings. errors.New("foo") and fmt.Errorf("foo") may be used interchangeably. + +If there are special circumstances where they come up again, the readability mentor might make an optional comment, but in general the author is free to pick the style they prefer in the given situation. + +Naturally, if anything not covered by the style guide does need more discussion, authors are welcome to ask – either in the specific review, or on internal message boards. + + +--- GO BEST PRACTICES --- + + +styleguide +Go Style Best Practices + +https://google.github.io/styleguide/go/best-practices + +Overview | Guide | Decisions | Best practices + +Note: This is part of a series of documents that outline Go Style at Google. This document is neither normative nor canonical, and is an auxiliary document to the core style guide. See the overview for more information. + +About + +This file documents guidance about how to best apply the Go Style Guide. This guidance is intended for common situations that arise frequently, but may not apply in every circumstance. Where possible, multiple alternative approaches are discussed along with the considerations that go into the decision about when and when not to apply them. + +See the overview for the full set of Style Guide documents. + +Naming + +Function and method names + +Avoid repetition + +When choosing the name for a function or method, consider the context in which the name will be read. Consider the following recommendations to avoid excess repetition at the call site: + +The following can generally be omitted from function and method names: + +The types of the inputs and outputs (when there is no collision) +The type of a method’s receiver +Whether an input or output is a pointer + +For functions, do not repeat the name of the package. + +// Bad: +package yamlconfig + +func ParseYAMLConfig(input string) (*Config, error) + +// Good: +package yamlconfig + +func Parse(input string) (*Config, error) + + +For methods, do not repeat the name of the method receiver. + +// Bad: +func (c *Config) WriteConfigTo(w io.Writer) (int64, error) + +// Good: +func (c *Config) WriteTo(w io.Writer) (int64, error) + + +Do not repeat the names of variables passed as parameters. + +// Bad: +func OverrideFirstWithSecond(dest, source *Config) error + +// Good: +func Override(dest, source *Config) error + + +Do not repeat the names and types of the return values. + +// Bad: +func TransformToJSON(input *Config) *jsonconfig.Config + +// Good: +func Transform(input *Config) *jsonconfig.Config + + +When it is necessary to disambiguate functions of a similar name, it is acceptable to include extra information. + +// Good: +func (c *Config) WriteTextTo(w io.Writer) (int64, error) +func (c *Config) WriteBinaryTo(w io.Writer) (int64, error) + + +Naming conventions + +There are some other common conventions when choosing names for functions and methods: + +Functions that return something are given noun-like names. + +// Good: +func (c *Config) JobName(key string) (value string, ok bool) + + +A corollary of this is that function and method names should avoid the prefix Get. + +// Bad: +func (c *Config) GetJobName(key string) (value string, ok bool) + + +Functions that do something are given verb-like names. + +// Good: +func (c *Config) WriteDetail(w io.Writer) (int64, error) + + +Identical functions that differ only by the types involved include the name of the type at the end of the name. + +// Good: +func ParseInt(input string) (int, error) +func ParseInt64(input string) (int64, error) +func AppendInt(buf []byte, value int) []byte +func AppendInt64(buf []byte, value int64) []byte + + +If there is a clear “primary” version, the type can be omitted from the name for that version: + +// Good: +func (c *Config) Marshal() ([]byte, error) +func (c *Config) MarshalText() (string, error) + + +Test double and helper packages + +There are several disciplines you can apply to naming packages and types that provide test helpers and especially test doubles. A test double could be a stub, fake, mock, or spy. + +These examples mostly use stubs. Update your names accordingly if your code uses fakes or another kind of test double. + +Suppose you have a well-focused package providing production code similar to this: + +package creditcard + +import ( + "errors" + + "path/to/money" +) + +// ErrDeclined indicates that the issuer declines the charge. +var ErrDeclined = errors.New("creditcard: declined") + +// Card contains information about a credit card, such as its issuer, +// expiration, and limit. +type Card struct { + // omitted +} + +// Service allows you to perform operations with credit cards against external +// payment processor vendors like charge, authorize, reimburse, and subscribe. +type Service struct { + // omitted +} + +func (s *Service) Charge(c *Card, amount money.Money) error { /* omitted */ } + + +Creating test helper packages + +Suppose you want to create a package that contains test doubles for another. We’ll use package creditcard (from above) for this example: + +One approach is to introduce a new Go package based on the production one for testing. A safe choice is to append the word test to the original package name (“creditcard” + “test”): + +// Good: +package creditcardtest + + +Unless stated explicitly otherwise, all examples in the sections below are in package creditcardtest. + +Simple case + +You want to add a set of test doubles for Service. Because Card is effectively a dumb data type, similar to a Protocol Buffer message, it needs no special treatment in tests, so no double is required. If you anticipate only test doubles for one type (like Service), you can take a concise approach to naming the doubles: + +// Good: +import ( + "path/to/creditcard" + "path/to/money" +) + +// Stub stubs creditcard.Service and provides no behavior of its own. +type Stub struct{} + +func (Stub) Charge(*creditcard.Card, money.Money) error { return nil } + + +This is strictly preferable to a naming choice like StubService or the very poor StubCreditCardService, because the base package name and its domain types imply what creditcardtest.Stub is. + +Finally, if the package is built with Bazel, make sure the new go_library rule for the package is marked as testonly: + +# Good: +go_library( + name = "creditcardtest", + srcs = ["creditcardtest.go"], + deps = [ + ":creditcard", + ":money", + ], + testonly = True, +) + + +The approach above is conventional and will be reasonably well understood by other engineers. + +See also: + +Go Tip #42: Authoring a Stub for Testing + +Multiple test double behaviors + +When one kind of stub is not enough (for example, you also need one that always fails), we recommend naming the stubs according to the behavior they emulate. Here we rename Stub to AlwaysCharges and introduce a new stub called AlwaysDeclines: + +// Good: +// AlwaysCharges stubs creditcard.Service and simulates success. +type AlwaysCharges struct{} + +func (AlwaysCharges) Charge(*creditcard.Card, money.Money) error { return nil } + +// AlwaysDeclines stubs creditcard.Service and simulates declined charges. +type AlwaysDeclines struct{} + +func (AlwaysDeclines) Charge(*creditcard.Card, money.Money) error { + return creditcard.ErrDeclined +} + + +Multiple doubles for multiple types + +But now suppose that package creditcard contains multiple types worth creating doubles for, as seen below with Service and StoredValue: + +package creditcard + +type Service struct { + // omitted +} + +type Card struct { + // omitted +} + +// StoredValue manages customer credit balances. This applies when returned +// merchandise is credited to a customer's local account instead of processed +// by the credit issuer. For this reason, it is implemented as a separate +// service. +type StoredValue struct { + // omitted +} + +func (s *StoredValue) Credit(c *Card, amount money.Money) error { /* omitted */ } + + +In this case, more explicit test double naming is sensible: + +// Good: +type StubService struct{} + +func (StubService) Charge(*creditcard.Card, money.Money) error { return nil } + +type StubStoredValue struct{} + +func (StubStoredValue) Credit(*creditcard.Card, money.Money) error { return nil } + + +Local variables in tests + +When variables in your tests refer to doubles, choose a name that most clearly differentiates the double from other production types based on context. Consider some production code you want to test: + +package payment + +import ( + "path/to/creditcard" + "path/to/money" +) + +type CreditCard interface { + Charge(*creditcard.Card, money.Money) error +} + +type Processor struct { + CC CreditCard +} + +var ErrBadInstrument = errors.New("payment: instrument is invalid or expired") + +func (p *Processor) Process(c *creditcard.Card, amount money.Money) error { + if c.Expired() { + return ErrBadInstrument + } + return p.CC.Charge(c, amount) +} + + +In the tests, a test double called a “spy” for CreditCard is juxtaposed against production types, so prefixing the name may improve clarity. + +// Good: +package payment + +import "path/to/creditcardtest" + +func TestProcessor(t *testing.T) { + var spyCC creditcardtest.Spy + proc := &Processor{CC: spyCC} + + // declarations omitted: card and amount + if err := proc.Process(card, amount); err != nil { + t.Errorf("proc.Process(card, amount) = %v, want nil", err) + } + + charges := []creditcardtest.Charge{ + {Card: card, Amount: amount}, + } + + if got, want := spyCC.Charges, charges; !cmp.Equal(got, want) { + t.Errorf("spyCC.Charges = %v, want %v", got, want) + } +} + + +This is clearer than when the name is not prefixed. + +// Bad: +package payment + +import "path/to/creditcardtest" + +func TestProcessor(t *testing.T) { + var cc creditcardtest.Spy + + proc := &Processor{CC: cc} + + // declarations omitted: card and amount + if err := proc.Process(card, amount); err != nil { + t.Errorf("proc.Process(card, amount) = %v, want nil", err) + } + + charges := []creditcardtest.Charge{ + {Card: card, Amount: amount}, + } + + if got, want := cc.Charges, charges; !cmp.Equal(got, want) { + t.Errorf("cc.Charges = %v, want %v", got, want) + } +} + + +Shadowing + +Note: This explanation uses two informal terms, stomping and shadowing. They are not official concepts in the Go language spec. + +Like many programming languages, Go has mutable variables: assigning to a variable changes its value. + +// Good: +func abs(i int) int { + if i < 0 { + i *= -1 + } + return i +} + + +When using short variable declarations with the := operator, in some cases a new variable is not created. We can call this stomping. It’s OK to do this when the original value is no longer needed. + +// Good: +// innerHandler is a helper for some request handler, which itself issues +// requests to other backends. +func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse { + // Unconditionally cap the deadline for this part of request handling. + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + ctxlog.Info(ctx, "Capped deadline in inner request") + + // Code here no longer has access to the original context. + // This is good style if when first writing this, you anticipate + // that even as the code grows, no operation legitimately should + // use the (possibly unbounded) original context that the caller provided. + + // ... +} + + +Be careful using short variable declarations in a new scope, though: that introduces a new variable. We can call this shadowing the original variable. Code after the end of the block refers to the original. Here is a buggy attempt to shorten the deadline conditionally: + +// Bad: +func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse { + // Attempt to conditionally cap the deadline. + if *shortenDeadlines { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + ctxlog.Info(ctx, "Capped deadline in inner request") + } + + // BUG: "ctx" here again means the context that the caller provided. + // The above buggy code compiled because both ctx and cancel + // were used inside the if statement. + + // ... +} + + +A correct version of the code might be: + +// Good: +func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse { + if *shortenDeadlines { + var cancel func() + // Note the use of simple assignment, = and not :=. + ctx, cancel = context.WithTimeout(ctx, 3*time.Second) + defer cancel() + ctxlog.Info(ctx, "Capped deadline in inner request") + } + // ... +} + + +In the case we called stomping, because there’s no new variable, the type being assigned must match that of the original variable. With shadowing, an entirely new entity is introduced so it can have a different type. Intentional shadowing can be a useful practice, but you can always use a new name if it improves clarity. + +It is not a good idea to use variables with the same name as standard packages other than very small scopes, because that renders free functions and values from that package inaccessible. Conversely, when picking a name for your package, avoid names that are likely to require import renaming or cause shadowing of otherwise good variable names at the client side. + +// Bad: +func LongFunction() { + url := "https://example.com/" + // Oops, now we can't use net/url in code below. +} + + +Util packages + +Go packages have a name specified on the package declaration, separate from the import path. The package name matters more for readability than the path. + +Go package names should be related to what the package provides. Naming a package just util, helper, common or similar is usually a poor choice (it can be used as part of the name though). Uninformative names make the code harder to read, and if used too broadly they are liable to cause needless import conflicts. + +Instead, consider what the callsite will look like. + +// Good: +db := spannertest.NewDatabaseFromFile(...) + +_, err := f.Seek(0, io.SeekStart) + +b := elliptic.Marshal(curve, x, y) + + +You can tell roughly what each of these do even without knowing the imports list (cloud.google.com/go/spanner/spannertest, io, and crypto/elliptic). With less focused names, these might read: + +// Bad: +db := test.NewDatabaseFromFile(...) + +_, err := f.Seek(0, common.SeekStart) + +b := helper.Marshal(curve, x, y) + + +Package size + +If you’re asking yourself how big your Go packages should be and whether to place related types in the same package or split them into different ones, a good place to start is the Go blog post about package names. Despite the post title, it’s not solely about naming. It contains some helpful hints and cites several useful articles and talks. + +Here are some other considerations and notes. + +Users see godoc for the package in one page, and any methods exported by types supplied by the package are grouped by their type. Godoc also group constructors along with the types they return. If client code is likely to need two values of different type to interact with each other, it may be convenient for the user to have them in the same package. + +Code within a package can access unexported identifiers in the package. If you have a few related types whose implementation is tightly coupled, placing them in the same package lets you achieve this coupling without polluting the public API with these details. A good test for this coupling is to imagine a hypothetical user of two packages, where the packages cover closely related topics: if the user must import both packages in order to use either in any meaningful way, combining them together is usually the right thing to do. The standard library generally demonstrates this kind of scoping and layering well. + +All of that being said, putting your entire project in a single package would likely make that package too large. When something is conceptually distinct, giving it its own small package can make it easier to use. The short name of the package as known to clients together with the exported type name work together to make a meaningful identifier: e.g. bytes.Buffer, ring.New. The Package Names blog post has more examples. + +Go style is flexible about file size, because maintainers can move code within a package from one file to another without affecting callers. But as a general guideline: it is usually not a good idea to have a single file with many thousands of lines in it, or having many tiny files. There is no “one type, one file” convention as in some other languages. As a rule of thumb, files should be focused enough that a maintainer can tell which file contains something, and the files should be small enough that it will be easy to find once there. The standard library often splits large packages to several source files, grouping related code by file. The source for package bytes is a good example. Packages with long package documentation may choose to dedicate one file called doc.go that has the package documentation, a package declaration, and nothing else, but this is not required. + +Within the Google codebase and in projects using Bazel, directory layout for Go code is different than it is in open source Go projects: you can have multiple go_library targets in a single directory. A good reason to give each package its own directory is if you expect to open source your project in the future. + +A few non-canonical reference examples to help demonstrate these ideas in action: + +small packages that contain one cohesive idea that warrant nothing more being added nor nothing being removed: + +package csv: CSV data encoding and decoding with responsibility split respectively between reader.go and writer.go. +package expvar: whitebox program telemetry all contained in expvar.go. + +moderately sized packages that contain one large domain and its multiple responsibilities together: + +package flag: command line flag management all contained in flag.go. + +large packages that divide several closely related domains across several files: + +package http: the core of HTTP: client.go, support for HTTP clients; server.go, support for HTTP servers; cookie.go, cookie management. +package os: cross-platform operating system abstractions: exec.go, subprocess management; file.go, file management; tempfile.go, temporary files. + +See also: + +Test double packages +Organizing Go Code (Blog Post) +Organizing Go Code (Presentation) + +Imports + +Protocol Buffer Messages and Stubs + +Proto library imports are treated differently than standard Go imports due to their cross-language nature. The convention for renamed proto imports are based on the rule that generated the package: + +The pb suffix is generally used for go_proto_library rules. +The grpc suffix is generally used for go_grpc_library rules. + +Often a single word describing the package is used: + +// Good: +import ( + foopb "path/to/package/foo_service_go_proto" + foogrpc "path/to/package/foo_service_go_grpc" +) + + +Follow the style guidance for package names. Prefer whole words. Short names are good, but avoid ambiguity. When in doubt, use the proto package name up to _go with a pb suffix: + +// Good: +import ( + pushqueueservicepb "path/to/package/push_queue_service_go_proto" +) + + +Note: Previous guidance encouraged very short names such as “xpb” or even just “pb”. New code should prefer more descriptive names. Existing code which uses short names should not be used as an example, but does not need to be changed. + +Import ordering + +See the Go Style Decisions: Import grouping. + +Error handling + +In Go, errors are values; they are created by code and consumed by code. Errors can be: + +Converted into diagnostic information for display to humans +Used by the maintainer +Interpreted by an end user + +Error messages also show up across a variety of different surfaces including log messages, error dumps, and rendered UIs. + +Code that processes (produces or consumes) errors should do so deliberately. It can be tempting to ignore or blindly propagate an error return value. However, it is always worth considering whether the current function in the call frame is positioned to handle the error most effectively. This is a large topic and it is hard to give categorical advice. Use your judgment, but keep the following considerations in mind: + +When creating an error value, decide whether to give it any structure. +When handling an error, consider adding information that you have but that the caller and/or callee might not. +See also guidance on error logging. + +While it is usually not appropriate to ignore an error, a reasonable exception to this is when orchestrating related operations, where often only the first error is useful. Package errgroup provides a convenient abstraction for a group of operations that can all fail or be canceled as a group. + +See also: + +Effective Go on errors +A post by the Go Blog on errors +Package errors +Package upspin.io/errors +GoTip #89: When to Use Canonical Status Codes as Errors +GoTip #48: Error Sentinel Values +GoTip #13: Designing Errors for Checking + +Error structure + +If callers need to interrogate the error (e.g., distinguish different error conditions), give the error value structure so that this can be done programmatically rather than having the caller perform string matching. This advice applies to production code as well as to tests that care about different error conditions. + +The simplest structured errors are unparameterized global values. + +type Animal string + +var ( + // ErrDuplicate occurs if this animal has already been seen. + ErrDuplicate = errors.New("duplicate") + + // ErrMarsupial occurs because we're allergic to marsupials outside Australia. + // Sorry. + ErrMarsupial = errors.New("marsupials are not supported") +) + +func process(animal Animal) error { + switch { + case seen[animal]: + return ErrDuplicate + case marsupial(animal): + return ErrMarsupial + } + seen[animal] = true + // ... + return nil +} + + +The caller can simply compare the returned error value of the function with one of the known error values: + +// Good: +func handlePet(...) { + switch err := process(an); err { + case ErrDuplicate: + return fmt.Errorf("feed %q: %v", an, err) + case ErrMarsupial: + // Try to recover with a friend instead. + alternate = an.BackupAnimal() + return handlePet(..., alternate, ...) + } +} + + +The above uses sentinel values, where the error must be equal (in the sense of ==) to the expected value. That is perfectly adequate in many cases. If process returns wrapped errors (discussed below), you can use errors.Is. + +// Good: +func handlePet(...) { + switch err := process(an); { + case errors.Is(err, ErrDuplicate): + return fmt.Errorf("feed %q: %v", an, err) + case errors.Is(err, ErrMarsupial): + // ... + } +} + + +Do not attempt to distinguish errors based on their string form. (See Go Tip #13: Designing Errors for Checking for more.) + +// Bad: +func handlePet(...) { + err := process(an) + if regexp.MatchString(`duplicate`, err.Error()) {...} + if regexp.MatchString(`marsupial`, err.Error()) {...} +} + + +If there is extra information in the error that the caller needs programmatically, it should ideally be presented structurally. For example, the os.PathError type is documented to place the pathname of the failing operation in a struct field which the caller can easily access. + +Other error structures can be used as appropriate, for example a project struct containing an error code and detail string. Package status is a common encapsulation; if you choose this approach (which you are not obligated to do), use canonical codes. See Go Tip #89: When to Use Canonical Status Codes as Errors to know if using status codes is the right choice. + +Adding information to errors + +When adding information to errors, avoid redundant information that the underlying error already provides. The os package, for instance, already includes path information in its errors. + +// Good: +if err := os.Open("settings.txt"); err != nil { + return fmt.Errorf("launch codes unavailable: %v", err) +} + +// Output: +// +// launch codes unavailable: open settings.txt: no such file or directory + + +Here, “launch codes unavailable” adds specific meaning to the os.Open error that’s relevant to the current function’s context, without duplicating the underlying file path information. + +// Bad: +if err := os.Open("settings.txt"); err != nil { + return fmt.Errorf("could not open settings.txt: %v", err) +} + +// Output: +// +// could not open settings.txt: open settings.txt: no such file or directory + + +Don’t add an annotation if its sole purpose is to indicate a failure without adding new information. The presence of an error sufficiently conveys the failure to the caller. + +// Bad: +return fmt.Errorf("failed: %v", err) // just return err instead + + +The choice between %v and %w when wrapping errors with fmt.Errorf is a nuanced decision that significantly impacts how errors are propagated handled, inspected, and documented within your application. The core principle is to make error values useful to their observers, whether those observers are humans or code. + +%v for simple annotation or new error + +The %v verb is your general-purpose tool for string formatting of any Go value, including errors. When used with fmt.Errorf, it embeds the string representation of an error (what its Error() method returns) into a new error value, dropping any structured information from the original error. Examples to use %v: + +Adding interesting, non-redundant context: as in the example above. + +Logging or displaying errors: When the primary goal is to present a human-readable error message in logs or to a user, and you don’t intend for the caller to programmatically errors.Is or errors.As the error (Note: errors.Unwrap is generally not recommended here as it doesn’t handle multi-errors). + +Creating fresh, independent errors: Sometimes it is necessary to transform an error into a new error message, thereby hiding the specifics of the original error. This practice is particularly beneficial at system boundaries, including but not limited to RPC, IPC, and storage, where we translate domain-specific errors into a canonical error space. + +// Good: +func (*FortuneTeller) SuggestFortune(context.Context, *pb.SuggestionRequest) (*pb.SuggestionResponse, error) { + // ... + if err != nil { + return nil, fmt.Errorf("couldn't find fortune database: %v", err) + } +} + + +We could also explicitly annotate RPC code Internal to the example above. + +// Good: +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (*FortuneTeller) SuggestFortune(context.Context, *pb.SuggestionRequest) (*pb.SuggestionResponse, error) { + // ... + if err != nil { + // Or use fmt.Errorf with the %w verb if deliberately wrapping an + // error which the caller is meant to unwrap. + return nil, status.Errorf(codes.Internal, "couldn't find fortune database", status.ErrInternal) + } +} + + +%w (wrap) for programmatic inspection and error chaining + +The %w verb is specifically designed for error wrapping. It creates a new error that provides an Unwrap() method, allowing callers to programmatically inspect the error chain using errors.Is and errors.As. Examples to use %w: + +Adding context while preserving the original error for programmatic inspection: This is the primary use case within helpers of your application. You want to enrich an error with additional context (e.g., what operation was being performed when it failed) but still allow the caller to check if the underlying error is a specific sentinel error or type. + +// Good: +func (s *Server) internalFunction(ctx context.Context) error { + // ... + if err != nil { + return fmt.Errorf("couldn't find remote file: %w", err) + } +} + + +This allows a higher-level function to do errors.Is(err, fs.ErrNotExist) if the underlying error was fs.ErrNotExist, even though it’s wrapped. + +At points where your system interacts with external systems like RPC, IPC, or storage, it’s often better to translate domain-specific errors into a standardized error space (e.g., gRPC status codes) rather than simply wrapping the raw underlying error with %w. The client typically doesn’t care about the exact internal file system error; they care about the canonical result (e.g., Internal, NotFound, PermissionDenied). + +When you explicitly document and test the underlying errors you expose: If your package’s API guarantees that certain underlying errors can be unwrapped and checked by callers (e.g., “this function might return ErrInvalidConfig wrapped within a more general error”), then %w is appropriate. This forms part of your package’s contract. + +See also: + +Error Documentation Conventions +Blog post on error wrapping + +Placement of %w in errors + +Prefer to place %w at the end of an error string if you are to use error wrapping with the %w formatting verb. + +Errors can be wrapped with the %w verb, or by placing them in a structured error that implements Unwrap() error (ex: fs.PathError). + +Wrapped errors form error chains: each new layer of wrapping adds a new entry to the front of the error chain. The error chain can be traversed with the Unwrap() error method. For example: + +err1 := fmt.Errorf("err1") +err2 := fmt.Errorf("err2: %w", err1) +err3 := fmt.Errorf("err3: %w", err2) + + +This forms an error chain of the form, + +flowchart LR + err3 == err3 wraps err2 ==> err2; + err2 == err2 wraps err1 ==> err1; + + +Regardless of where the %w verb is placed, the error returned always represents the front of the error chain, and the %w is the next child. Similarly, Unwrap() error always traverses the error chain from newest to oldest error. + +Placement of the %w verb does, however, affect whether the error chain is printed newest to oldest, oldest to newest, or neither: + +// Good: +err1 := fmt.Errorf("err1") +err2 := fmt.Errorf("err2: %w", err1) +err3 := fmt.Errorf("err3: %w", err2) +fmt.Println(err3) // err3: err2: err1 +// err3 is a newest-to-oldest error chain, that prints newest-to-oldest. + +// Bad: +err1 := fmt.Errorf("err1") +err2 := fmt.Errorf("%w: err2", err1) +err3 := fmt.Errorf("%w: err3", err2) +fmt.Println(err3) // err1: err2: err3 +// err3 is a newest-to-oldest error chain, that prints oldest-to-newest. + +// Bad: +err1 := fmt.Errorf("err1") +err2 := fmt.Errorf("err2-1 %w err2-2", err1) +err3 := fmt.Errorf("err3-1 %w err3-2", err2) +fmt.Println(err3) // err3-1 err2-1 err1 err2-2 err3-2 +// err3 is a newest-to-oldest error chain, that neither prints newest-to-oldest +// nor oldest-to-newest. + + +Therefore, in order for error text to mirror error chain structure, prefer placing the %w verb at the end with the form [...]: %w. + +Sentinel error placement + +An exception to this rule is when wrapping sentinel errors. A sentinel error is an error that serves as a primary categorization of a failure. This helps observers quickly understand the nature of a failure (such as “not found” or “invalid argument”) without having to parse the entire error message. Identifying that error type as early as possible in the error string is beneficial. + +Examples of sentinel errors include os errors (e.g., os.ErrInvalid) and package-level errors. + +In these cases, placing the %w verb at the beginning of the error string can improve readability by immediately identifying the category of the error. + +// Good: +package parser + +var ErrParse = fmt.Errorf("parse error") + +// This is another package error that could be returned. +var ErrParseInvalidHeader = fmt.Errorf("%w: invalid header", ErrParse) + +func parseHeader() error { + err := checkHeader() + return fmt.Errorf("%w: invalid character in header: %v", ErrParseInvalidHeader, err) +} + +err := fmt.Errorf("%w: couldn't find fortune database: %v", ErrInternal, err) + + +Placing the status at the beginning ensures that the most relevant categorical information is most prominent. + +// Bad: +package parser + +var ErrParse = fmt.Errorf("parse error") + +// This is another package error that could be returned. +var ErrParseInvalidHeader = fmt.Errorf("%w: invalid header", ErrParse) + +func parseHeader() error { + err := checkHeader() + return fmt.Errorf("invalid character in header: %v: %w", err, ErrParseInvalidHeader) +} + +var ErrInternal = status.Error(codes.Internal, "internal") +err2 := fmt.Errorf("couldn't find fortune database: %v: %w", err, ErrInternal) + + +When you place it at the end, it makes it harder to identify the error category when reading the error text, as it’s buried in the specific error details. + +See also: + +Go Tip #48: Error Sentinel Values +Go Tip #106: Error Naming Conventions + +Logging errors + +Functions sometimes need to tell an external system about an error without propagating it to their callers. Logging is an obvious choice here; but be conscious of what and how you log errors. + +Like good test failure messages, log messages should clearly express what went wrong and help the maintainer by including relevant information to diagnose the problem. + +Avoid duplication. If you return an error, it’s usually better not to log it yourself but rather let the caller handle it. The caller can choose to log the error, or perhaps rate-limit logging using rate.Sometimes. Other options include attempting recovery or even stopping the program. In any case, giving the caller control helps avoid logspam. + +The downside to this approach, however, is that any logging is written using the caller’s line coordinates. + +Be careful with PII. Many log sinks are not appropriate destinations for sensitive end-user information. + +Use log.Error sparingly. ERROR level logging causes a flush and is more expensive than lower logging levels. This can have serious performance impact on your code. When deciding between error and warning levels, consider the best practice that messages at the error level should be actionable rather than “more serious” than a warning. + +Inside Google, we have monitoring systems that can be set up for more effective alerting than writing to a log file and hoping someone notices it. This is similar but not identical to the standard library package expvar. + +Custom verbosity levels + +Use verbose logging (log.V) to your advantage. Verbose logging can be useful for development and tracing. Establishing a convention around verbosity levels can be helpful. For example: + +Write a small amount of extra information at V(1) +Trace more information in V(2) +Dump large internal states in V(3) + +To minimize the cost of verbose logging, you should ensure not to accidentally call expensive functions even when log.V is turned off. log.V offers two APIs. The more convenient one carries the risk of this accidental expense. When in doubt, use the slightly more verbose style. + +// Good: +for _, sql := range queries { + log.V(1).Infof("Handling %v", sql) + if log.V(2) { + log.Infof("Handling %v", sql.Explain()) + } + sql.Run(...) +} + +// Bad: +// sql.Explain called even when this log is not printed. +log.V(2).Infof("Handling %v", sql.Explain()) + + +Program initialization + +Program initialization errors (such as bad flags and configuration) should be propagated upward to main, which should call log.Exit with an error that explains how to fix the error. In these cases, log.Fatal should not generally be used, because a stack trace that points at the check is not likely to be as useful as a human-generated, actionable message. + +Program checks and panics + +As stated in the decision against panics, standard error handling should be structured around error return values. Libraries should prefer returning an error to the caller rather than aborting the program, especially for transient errors. + +It is occasionally necessary to perform consistency checks on an invariant and terminate the program if it is violated. In general, this is only done when a failure of the invariant check means that the internal state has become unrecoverable. The most reliable way to do this in the Google codebase is to call log.Fatal. Using panic in these cases is not reliable, because it is possible for deferred functions to deadlock or further corrupt internal or external state. + +Similarly, resist the temptation to recover panics to avoid crashes, as doing so can result in propagating a corrupted state. The further you are from the panic, the less you know about the state of the program, which could be holding locks or other resources. The program can then develop other unexpected failure modes that can make the problem even more difficult to diagnose. Instead of trying to handle unexpected panics in code, use monitoring tools to surface unexpected failures and fix related bugs with a high priority. + +Note: The standard net/http server violates this advice and recovers panics from request handlers. Consensus among experienced Go engineers is that this was a historical mistake. If you sample server logs from application servers in other languages, it is common to find large stacktraces that are left unhandled. Avoid this pitfall in your servers. + +When to panic + +The standard library panics on API misuse. For example, reflect issues a panic in many cases where a value is accessed in a way that suggests it was misinterpreted. This is analogous to the panics on core language bugs such as accessing an element of a slice that is out of bounds. Code review and tests should discover such bugs, which are not expected to appear in production code. These panics act as invariant checks that do not depend on a library, as the standard library does not have access to the levelled log package that the Google codebase uses. + +Another case in which panics can be useful, though uncommon, is as an internal implementation detail of a package which always has a matching recover in the callchain. Parsers and similar deeply nested, tightly coupled internal function groups can benefit from this design, where plumbing error returns adds complexity without value. + +The key attribute of this design is that these panics are never allowed to escape across package boundaries and do not form part of the package’s API. This is typically accomplished with a top-level deferred function that uses recover to translate a propagated panic into a returned error at the public API boundary. It requires the code that panics and recovers to distinguish between panics that the code raises itself and those that it doesn’t: + +// Good: +type syntaxError struct { + msg string +} + +func parseInt(in string) int { + n, err := strconv.Atoi(in) + if err != nil { + panic(&syntaxError{"not a valid integer"}) + } +} + +func Parse(in string) (_ *Node, err error) { + defer func() { + if p := recover(); p != nil { + sErr, ok := p.(*syntaxError) + if !ok { + panic(p) // Propagate the panic since it is outside our code's domain. + } + err = fmt.Errorf("syntax error: %v", sErr.msg) + } + }() + ... // Parse input calling parseInt internally to parse integers +} + + +Warning: Code employing this pattern must take care to manage any resources associated with the code run in such defer-managed sections (e.g., close, free, or unlock). + +See: Go Tip #81: Avoiding Resource Leaks in API Design + +Panic is also used when the compiler cannot identify unreachable code, for example when using a function like log.Fatal that will not return: + +// Good: +func answer(i int) string { + switch i { + case 42: + return "yup" + case 54: + return "base 13, huh" + default: + log.Fatalf("Sorry, %d is not the answer.", i) + panic("unreachable") + } +} + + +Do not call log functions before flags have been parsed. If you must die in a package initialization function (an init or a “must” function), a panic is acceptable in place of the fatal logging call. + +See also: + +Handling panics and Run-time Panics in the language specification +Defer, Panic, and Recover +On the uses and misuses of panics in Go + +Documentation + +Conventions + +This section augments the decisions document’s commentary section. + +Go code that is documented in familiar style is easier to read and less likely to be misused than something misdocumented or not documented at all. Runnable examples show up in Godoc and Code Search and are an excellent way of explaining how to use your code. + +Parameters and configuration + +Not every parameter must be enumerated in the documentation. This applies to: + +function and method parameters +struct fields +APIs for options + +Document the error-prone or non-obvious fields and parameters by saying why they are interesting. + +In the following snippet, the highlighted commentary adds little useful information to the reader: + +// Bad: +// Sprintf formats according to a format specifier and returns the resulting +// string. +// +// format is the format, and data is the interpolation data. +func Sprintf(format string, data ...any) string + + +However, this snippet demonstrates a code scenario similar to the previous where the commentary instead states something non-obvious or materially helpful to the reader: + +// Good: +// Sprintf formats according to a format specifier and returns the resulting +// string. +// +// The provided data is used to interpolate the format string. If the data does +// not match the expected format verbs or the amount of data does not satisfy +// the format specification, the function will inline warnings about formatting +// errors into the output string as described by the Format errors section +// above. +func Sprintf(format string, data ...any) string + + +Consider your likely audience in choosing what to document and at what depth. Maintainers, newcomers to the team, external users, and even yourself six months in the future may appreciate slightly different information from what is on your mind when you first come to write your docs. + +See also: + +GoTip #41: Identify Function Call Parameters +GoTip #51: Patterns for Configuration + +Contexts + +It is implied that the cancellation of a context argument interrupts the function it is provided to. If the function can return an error, conventionally it is ctx.Err(). + +This fact does not need to be restated: + +// Bad: +// Run executes the worker's run loop. +// +// The method will process work until the context is cancelled and accordingly +// returns an error. +func (Worker) Run(ctx context.Context) error + + +Because that is implied, the following is better: + +// Good: +// Run executes the worker's run loop. +func (Worker) Run(ctx context.Context) error + + +Where context behavior is different or non-obvious, it should be expressly documented if any of the following are true. + +The function returns an error other than ctx.Err() when the context is cancelled: + +// Good: +// Run executes the worker's run loop. +// +// If the context is cancelled, Run returns a nil error. +func (Worker) Run(ctx context.Context) error + + +The function has other mechanisms that may interrupt it or affect lifetime: + +// Good: +// Run executes the worker's run loop. +// +// Run processes work until the context is cancelled or Stop is called. +// Context cancellation is handled asynchronously internally: run may return +// before all work has stopped. The Stop method is synchronous and waits +// until all operations from the run loop finish. Use Stop for graceful +// shutdown. +func (Worker) Run(ctx context.Context) error + +func (Worker) Stop() + + +The function has special expectations about context lifetime, lineage, or attached values: + +// Good: +// NewReceiver starts receiving messages sent to the specified queue. +// The context should not have a deadline. +func NewReceiver(ctx context.Context) *Receiver + +// Principal returns a human-readable name of the party who made the call. +// The context must have a value attached to it from security.NewContext. +func Principal(ctx context.Context) (name string, ok bool) + + +Warning: Avoid designing APIs that make such demands (like contexts not having deadlines) from their callers. The above is only an example of how to document this if it cannot be avoided, not an endorsement of the pattern. + +Concurrency + +Go users assume that conceptually read-only operations are safe for concurrent use and do not require extra synchronization. + +The extra remark about concurrency can safely be removed in this Godoc: + +// Len returns the number of bytes of the unread portion of the buffer; +// b.Len() == len(b.Bytes()). +// +// It is safe to be called concurrently by multiple goroutines. +func (*Buffer) Len() int + + +Mutating operations, however, are not assumed to be safe for concurrent use and require the user to consider synchronization. + +Similarly, the extra remark about concurrency can safely be removed here: + +// Grow grows the buffer's capacity. +// +// It is not safe to be called concurrently by multiple goroutines. +func (*Buffer) Grow(n int) + + +Documentation is strongly encouraged if any of the following are true. + +It is unclear whether the operation is read-only or mutating: + +// Good: +package lrucache + +// Lookup returns the data associated with the key from the cache. +// +// This operation is not safe for concurrent use. +func (*Cache) Lookup(key string) (data []byte, ok bool) + + +Why? A cache hit when looking up the key mutate a LRU cache internally. How this is implemented may not be obvious to all readers. + +Synchronization is provided by the API: + +// Good: +package fortune_go_proto + +// NewFortuneTellerClient returns an *rpc.Client for the FortuneTeller service. +// It is safe for simultaneous use by multiple goroutines. +func NewFortuneTellerClient(cc *rpc.ClientConn) *FortuneTellerClient + + +Why? Stubby provides synchronization. + +Note: If the API is a type and the API provides synchronization in entirety, conventionally only the type definition documents the semantics. + +The API consumes user-implemented types of interfaces, and the interface’s consumer has particular concurrency requirements: + +// Good: +package health + +// A Watcher reports the health of some entity (usually a backend service). +// +// Watcher methods are safe for simultaneous use by multiple goroutines. +type Watcher interface { + // Watch sends true on the passed-in channel when the Watcher's + // status has changed. + Watch(changed chan<- bool) (unwatch func()) + + // Health returns nil if the entity being watched is healthy, or a + // non-nil error explaining why the entity is not healthy. + Health() error +} + + +Why? Whether an API is safe for use by multiple goroutines is part of its contract. + +Cleanup + +Document any explicit cleanup requirements that the API has. Otherwise, callers won’t use the API correctly, leading to resource leaks and other possible bugs. + +Call out cleanups that are up to the caller: + +// Good: +// NewTicker returns a new Ticker containing a channel that will send the +// current time on the channel after each tick. +// +// Call Stop to release the Ticker's associated resources when done. +func NewTicker(d Duration) *Ticker + +func (*Ticker) Stop() + + +If it is potentially unclear how to clean up the resources, explain how: + +// Good: +// Get issues a GET to the specified URL. +// +// When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +// +// resp, err := http.Get("http://example.com/") +// if err != nil { +// // handle error +// } +// defer resp.Body.Close() +// body, err := io.ReadAll(resp.Body) +func (c *Client) Get(url string) (resp *Response, err error) + + +See also: + +GoTip #110: Don’t Mix Exit With Defer + +Errors + +Document significant error sentinel values or error types that your functions return to callers so that callers can anticipate what types of conditions they can handle in their code. + +// Good: +package os + +// Read reads up to len(b) bytes from the File and stores them in b. It returns +// the number of bytes read and any error encountered. +// +// At end of file, Read returns 0, io.EOF. +func (*File) Read(b []byte) (n int, err error) { + + +When a function returns a specific error type, correctly note whether the error is a pointer receiver or not: + +// Good: +package os + +type PathError struct { + Op string + Path string + Err error +} + +// Chdir changes the current working directory to the named directory. +// +// If there is an error, it will be of type *PathError. +func Chdir(dir string) error { + + +Documenting whether the values returned are pointer receivers enables callers to correctly compare the errors using errors.Is, errors.As, and package cmp. This is because a non-pointer value is not equivalent to a pointer value. + +Note: In the Chdir example, the return type is written as error rather than *PathError due to how nil interface values work. + +Document overall error conventions in the package’s documentation when the behavior is applicable to most errors found in the package: + +// Good: +// Package os provides a platform-independent interface to operating system +// functionality. +// +// Often, more information is available within the error. For example, if a +// call that takes a file name fails, such as Open or Stat, the error will +// include the failing file name when printed and will be of type *PathError, +// which may be unpacked for more information. +package os + + +Thoughtful application of these approaches can add extra information to errors without much effort and help callers avoid adding redundant annotations. + +See also: + +Go Tip #106: Error Naming Conventions +Go Tip #89: When to Use Canonical Status Codes as Errors + +Preview + +Go features a documentation server. It is recommended to preview the documentation your code produces both before and during the code review process. This helps to validate that the godoc formatting is rendered correctly. + +Godoc formatting + +Godoc provides some specific syntax to format documentation. + +A blank line is required to separate paragraphs: + +// Good: +// LoadConfig reads a configuration out of the named file. +// +// See some/shortlink for config file format details. + + +Test files can contain runnable examples that appear attached to the corresponding documentation in godoc: + +// Good: +func ExampleConfig_WriteTo() { + cfg := &Config{ + Name: "example", + } + if err := cfg.WriteTo(os.Stdout); err != nil { + log.Exitf("Failed to write config: %s", err) + } + // Output: + // { + // "name": "example" + // } +} + + +Indenting lines by an additional two spaces formats them verbatim: + +// Good: +// Update runs the function in an atomic transaction. +// +// This is typically used with an anonymous TransactionFunc: +// +// if err := db.Update(func(state *State) { state.Foo = bar }); err != nil { +// //... +// } + + +Note, however, that it can often be more appropriate to put code in a runnable example instead of including it in a comment. + +This verbatim formatting can be leveraged for formatting that is not native to godoc, such as lists and tables: + +// Good: +// LoadConfig reads a configuration out of the named file. +// +// LoadConfig treats the following keys in special ways: +// "import" will make this configuration inherit from the named file. +// "env" if present will be populated with the system environment. + + +A single line that begins with a capital letter, contains no punctuation except parentheses and commas, and is followed by another paragraph, is formatted as a header: + +// Good: +// The following line is formatted as a heading. +// +// Using headings +// +// Headings come with autogenerated anchor tags for easy linking. + + +Signal boosting + +Sometimes a line of code looks like something common, but actually isn’t. One of the best examples of this is an err == nil check (since err != nil is much more common). The following two conditional checks are hard to distinguish: + +// Good: +if err := doSomething(); err != nil { + // ... +} + +// Bad: +if err := doSomething(); err == nil { + // ... +} + + +You can instead “boost” the signal of the conditional by adding a comment: + +// Good: +if err := doSomething(); err == nil { // if NO error + // ... +} + + +The comment draws attention to the difference in the conditional. + +Variable declarations + +Initialization + +For consistency, prefer := over var when initializing a new variable with a non-zero value. + +// Good: +i := 42 + +// Bad: +var i = 42 + + +Declaring variables with zero values + +The following declarations use the zero value: + +// Good: +var ( + coords Point + magic [4]byte + primes []int +) + + +You should declare values using the zero value when you want to convey an empty value that is ready for later use. Using composite literals with explicit initialization can be clunky: + +// Bad: +var ( + coords = Point{X: 0, Y: 0} + magic = [4]byte{0, 0, 0, 0} + primes = []int(nil) +) + + +A common application of zero value declaration is when using a variable as the output when unmarshalling: + +// Good: +var coords Point +if err := json.Unmarshal(data, &coords); err != nil { + + +It is also okay to use the zero value in the following form when you need a variable of a pointer type: + +// Good: +msg := new(pb.Bar) // or "&pb.Bar{}" +if err := proto.Unmarshal(data, msg); err != nil { + + +If you need a lock or other field that must not be copied in your struct, you can make it a value type to take advantage of zero value initialization. It does mean that the containing type must now be passed via a pointer and not a value. Methods on the type must take pointer receivers. + +// Good: +type Counter struct { + // This field does not have to be "*sync.Mutex". However, + // users must now pass *Counter objects between themselves, not Counter. + mu sync.Mutex + data map[string]int64 +} + +// Note this must be a pointer receiver to prevent copying. +func (c *Counter) IncrementBy(name string, n int64) + + +It’s acceptable to use value types for local variables of composites (such as structs and arrays) even if they contain such uncopyable fields. However, if the composite is returned by the function, or if all accesses to it end up needing to take an address anyway, prefer declaring the variable as a pointer type at the outset. Similarly, protobuf messages should be declared as pointer types. + +// Good: +func NewCounter(name string) *Counter { + c := new(Counter) // "&Counter{}" is also fine. + registerCounter(name, c) + return c +} + +var msg = new(pb.Bar) // or "&pb.Bar{}". + + +This is because *pb.Something satisfies proto.Message while pb.Something does not. + +// Bad: +func NewCounter(name string) *Counter { + var c Counter + registerCounter(name, &c) + return &c +} + +var msg = pb.Bar{} + + +Important: Map types must be explicitly initialized before they can be modified. However, reading from zero-value maps is perfectly fine. + +For map and slice types, if the code is particularly performance sensitive and if you know the sizes in advance, see the size hints section. + +Composite literals + +The following are composite literal declarations: + +// Good: +var ( + coords = Point{X: x, Y: y} + magic = [4]byte{'I', 'W', 'A', 'D'} + primes = []int{2, 3, 5, 7, 11} + captains = map[string]string{"Kirk": "James Tiberius", "Picard": "Jean-Luc"} +) + + +You should declare a value using a composite literal when you know initial elements or members. + +In contrast, using composite literals to declare empty or memberless values can be visually noisy compared to zero-value initialization. + +When you need a pointer to a zero value, you have two options: empty composite literals and new. Both are fine, but the new keyword can serve to remind the reader that if a non-zero value were needed, a composite literal wouldn’t work: + +// Good: +var ( + buf = new(bytes.Buffer) // non-empty Buffers are initialized with constructors. + msg = new(pb.Message) // non-empty proto messages are initialized with builders or by setting fields one by one. +) + + +Size hints + +The following are declarations that take advantage of size hints in order to preallocate capacity: + +// Good: +var ( + // Preferred buffer size for target filesystem: st_blksize. + buf = make([]byte, 131072) + // Typically process up to 8-10 elements per run (16 is a safe assumption). + q = make([]Node, 0, 16) + // Each shard processes shardSize (typically 32000+) elements. + seen = make(map[string]bool, shardSize) +) + + +Size hints and preallocation are important steps when combined with empirical analysis of the code and its integrations, to create performance-sensitive and resource-efficient code. + +Most code does not need a size hint or preallocation, and can allow the runtime to grow the slice or map as necessary. It is acceptable to preallocate when the final size is known (e.g. when converting between a map and a slice) but this is not a readability requirement, and may not be worth the clutter in small cases. + +Warning: Preallocating more memory than you need can waste memory in the fleet or even harm performance. When in doubt, see GoTip #3: Benchmarking Go Code and default to a zero initialization or a composite literal declaration. + +Channel direction + +Specify channel direction where possible. + +// Good: +// sum computes the sum of all of the values. It reads from the channel until +// the channel is closed. +func sum(values <-chan int) int { + // ... +} + + +This prevents casual programming errors that are possible without specification: + +// Bad: +func sum(values chan int) (out int) { + for v := range values { + out += v + } + // values must already be closed for this code to be reachable, which means + // a second close triggers a panic. + close(values) +} + + +When the direction is specified, the compiler catches simple errors like this. It also helps to convey a measure of ownership to the type. + +See also Bryan Mills’ talk “Rethinking Classical Concurrency Patterns”: slides video. + +Function argument lists + +Don’t let the signature of a function get too long. As more parameters are added to a function, the role of individual parameters becomes less clear, and adjacent parameters of the same type become easier to confuse. Functions with large numbers of arguments are less memorable and more difficult to read at the call-site. + +When designing an API, consider splitting a highly configurable function whose signature is growing complex into several simpler ones. These can share an (unexported) implementation if necessary. + +Where a function requires many inputs, consider introducing an option struct for some of the arguments or employing the more advanced variadic options technique. The primary consideration for which strategy to choose should be how the function call looks across all expected use cases. + +The recommendations below primarily apply to exported APIs, which are held to a higher standard than unexported ones. These techniques may be unnecessary for your use case. Use your judgment, and balance the principles of clarity and least mechanism. + +See also: Go Tip #24: Use Case-Specific Constructions + +Option structure + +An option structure is a struct type that collects some or all of the arguments of a function or method, that is then passed as the last argument to the function or method. (The struct should be exported only if it is used in an exported function.) + +Using an option structure has a number of benefits: + +The struct literal includes both fields and values for each argument, which makes them self-documenting and harder to swap. +Irrelevant or “default” fields can be omitted. +Callers can share the option struct and write helpers to operate on it. +Structs provide cleaner per-field documentation than function arguments. +Option structs can grow over time without impacting call-sites. + +Here is an example of a function that could be improved: + +// Bad: +func EnableReplication(ctx context.Context, config *replicator.Config, primaryRegions, readonlyRegions []string, replicateExisting, overwritePolicies bool, replicationInterval time.Duration, copyWorkers int, healthWatcher health.Watcher) { + // ... +} + + +The function above could be rewritten with an option structure as follows: + +// Good: +type ReplicationOptions struct { + Config *replicator.Config + PrimaryRegions []string + ReadonlyRegions []string + ReplicateExisting bool + OverwritePolicies bool + ReplicationInterval time.Duration + CopyWorkers int + HealthWatcher health.Watcher +} + +func EnableReplication(ctx context.Context, opts ReplicationOptions) { + // ... +} + + +The function can then be called in a different package: + +// Good: +func foo(ctx context.Context) { + // Complex call: + storage.EnableReplication(ctx, storage.ReplicationOptions{ + Config: config, + PrimaryRegions: []string{"us-east1", "us-central2", "us-west3"}, + ReadonlyRegions: []string{"us-east5", "us-central6"}, + OverwritePolicies: true, + ReplicationInterval: 1 * time.Hour, + CopyWorkers: 100, + HealthWatcher: watcher, + }) + + // Simple call: + storage.EnableReplication(ctx, storage.ReplicationOptions{ + Config: config, + PrimaryRegions: []string{"us-east1", "us-central2", "us-west3"}, + }) +} + + +Note: Contexts are never included in option structs. + +This option is often preferred when some of the following apply: + +All callers need to specify one or more of the options. +A large number of callers need to provide many options. +The options are shared between multiple functions that the user will call. + +Variadic options + +Using variadic options, exported functions are created which return closures that can be passed to the variadic (...) parameter of a function. The function takes as its parameters the values of the option (if any), and the returned closure accepts a mutable reference (usually a pointer to a struct type) that will be updated based on the inputs. + +Using variadic options can provide a number of benefits: + +Options take no space at a call-site when no configuration is needed. +Options are still values, so callers can share them, write helpers, and accumulate them. +Options can accept multiple parameters (e.g. cartesian.Translate(dx, dy int) TransformOption). +The option functions can return a named type to group options together in godoc. +Packages can allow (or prevent) third-party packages to define (or from defining) their own options. + +Note: Using variadic options requires a substantial amount of additional code (see the following example), so it should only be used when the advantages outweigh the overhead. + +Here is an example of a function that could be improved: + +// Bad: +func EnableReplication(ctx context.Context, config *placer.Config, primaryCells, readonlyCells []string, replicateExisting, overwritePolicies bool, replicationInterval time.Duration, copyWorkers int, healthWatcher health.Watcher) { + ... +} + + +The example above could be rewritten with variadic options as follows: + +// Good: +type replicationOptions struct { + readonlyCells []string + replicateExisting bool + overwritePolicies bool + replicationInterval time.Duration + copyWorkers int + healthWatcher health.Watcher +} + +// A ReplicationOption configures EnableReplication. +type ReplicationOption func(*replicationOptions) + +// ReadonlyCells adds additional cells that should additionally +// contain read-only replicas of the data. +// +// Passing this option multiple times will add additional +// read-only cells. +// +// Default: none +func ReadonlyCells(cells ...string) ReplicationOption { + return func(opts *replicationOptions) { + opts.readonlyCells = append(opts.readonlyCells, cells...) + } +} + +// ReplicateExisting controls whether files that already exist in the +// primary cells will be replicated. Otherwise, only newly-added +// files will be candidates for replication. +// +// Passing this option again will overwrite earlier values. +// +// Default: false +func ReplicateExisting(enabled bool) ReplicationOption { + return func(opts *replicationOptions) { + opts.replicateExisting = enabled + } +} + +// ... other options ... + +// DefaultReplicationOptions control the default values before +// applying options passed to EnableReplication. +var DefaultReplicationOptions = []ReplicationOption{ + OverwritePolicies(true), + ReplicationInterval(12 * time.Hour), + CopyWorkers(10), +} + +func EnableReplication(ctx context.Context, config *placer.Config, primaryCells []string, opts ...ReplicationOption) { + var options replicationOptions + for _, opt := range DefaultReplicationOptions { + opt(&options) + } + for _, opt := range opts { + opt(&options) + } +} + + +The function can then be called in a different package: + +// Good: +func foo(ctx context.Context) { + // Complex call: + storage.EnableReplication(ctx, config, []string{"po", "is", "ea"}, + storage.ReadonlyCells("ix", "gg"), + storage.OverwritePolicies(true), + storage.ReplicationInterval(1*time.Hour), + storage.CopyWorkers(100), + storage.HealthWatcher(watcher), + ) + + // Simple call: + storage.EnableReplication(ctx, config, []string{"po", "is", "ea"}) +} + + +Prefer this option when many of the following apply: + +Most callers will not need to specify any options. +Most options are used infrequently. +There are a large number of options. +Options require arguments. +Options could fail or be set incorrectly (in which case the option function returns an error). +Options require a lot of documentation that can be hard to fit in a struct. +Users or other packages can provide custom options. + +Options in this style should accept parameters rather than using presence to signal their value; the latter can make dynamic composition of arguments much more difficult. For example, binary settings should accept a boolean (e.g. rpc.FailFast(enable bool) is preferable to rpc.EnableFailFast()). An enumerated option should accept an enumerated constant (e.g. log.Format(log.Capacitor) is preferable to log.CapacitorFormat()). The alternative makes it much more difficult for users who must programmatically choose which options to pass; such users are forced to change the actual composition of the parameters rather than simply changing the arguments to the options. Don’t assume that all users will know the full set of options statically. + +In general, options should be processed in order. If there is a conflict or if a non-cumulative option is passed multiple times, the last argument should win. + +The parameter to the option function is generally unexported in this pattern, to restrict the options to being defined only within the package itself. This is a good default, though there may be times when it is appropriate to allow other packages to define options. + +See Rob Pike’s original blog post and Dave Cheney’s talk for a more in-depth look at how these options can be used. + +Complex command-line interfaces + +Some programs wish to present users with a rich command-line interface that includes sub-commands. For example, kubectl create, kubectl run, and many other sub-commands are all provided by the program kubectl. There are at least the following libraries in common use for achieving this. + +If you don’t have a preference or other considerations are equal, subcommands is recommended, since it is the simplest and is easy to use correctly. However, if you need different features that it doesn’t provide, pick one of the other options. + +cobra + +Flag convention: getopt +Common outside the Google codebase. +Many extra features. +Pitfalls in usage (see below). + +subcommands + +Flag convention: Go +Simple and easy to use correctly. +Recommended if you don’t need extra features. + +Warning: cobra command functions should use cmd.Context() to obtain a context rather than creating their own root context with context.Background. Code that uses the subcommands package already receives the correct context as a function parameter. + +You are not required to place each subcommand in a separate package, and it is often not necessary to do so. Apply the same considerations about package boundaries as in any Go codebase. If your code can be used both as a library and as a binary, it is usually beneficial to separate the CLI code and the library, making the CLI just one more of its clients. (This is not specific to CLIs that have subcommands, but is mentioned here because it is a common place where it comes up.) + +Tests + +Leave testing to the Test function + +Go distinguishes between “test helpers” and “assertion helpers”: + +Test helpers are functions that do setup or cleanup tasks. All failures that occur in test helpers are expected to be failures of the environment (not from the code under test) — for example when a test database cannot be started because there are no more free ports on this machine. For functions like these, calling t.Helper is often appropriate to mark them as a test helper. See error handling in test helpers for more details. + +Assertion helpers are functions that check the correctness of a system and fail the test if an expectation is not met. Assertion helpers are not considered idiomatic in Go. + +The purpose of a test is to report pass/fail conditions of the code under test. The ideal place to fail a test is within the Test function itself, as that ensures that failure messages and the test logic are clear. + +As your testing code grows, it may become necessary to factor out some functionality to separate functions. Standard software engineering considerations still apply, as test code is still code. If the functionality does not interact with the testing framework, then all of the usual rules apply. When the common code interacts with the framework, however, some care must be taken to avoid common pitfalls that can lead to uninformative failure messages and unmaintainable tests. + +If many separate test cases require the same validation logic, arrange the test in one of the following ways instead of using assertion helpers or complex validation functions: + +Inline the logic (both the validation and the failure) in the Test function, even if it is repetitive. This works best in simple cases. +If inputs are similar, consider unifying them into a table-driven test while keeping the logic inlined in the loop. This helps to avoid repetition while keeping the validation and failure in the Test. +If there are multiple callers who need the same validation function but table tests are not suitable (typically because the inputs are not simple enough or the validation is required as part of a sequence of operations), arrange the validation function so that it returns a value (typically an error) rather than taking a testing.T parameter and using it to fail the test. Use logic within the Test to decide whether to fail, and to provide useful test failures. You can also create test helpers to factor out common boilerplate setup code. + +The design outlined in the last point maintains orthogonality. For example, package cmp is not designed to fail tests, but rather to compare (and to diff) values. It therefore does not need to know about the context in which the comparison was made, since the caller can supply that. If your common testing code provides a cmp.Transformer for your data type, that can often be the simplest design. For other validations, consider returning an error value. + +// Good: +// polygonCmp returns a cmp.Option that equates s2 geometry objects up to +// some small floating-point error. +func polygonCmp() cmp.Option { + return cmp.Options{ + cmp.Transformer("polygon", func(p *s2.Polygon) []*s2.Loop { return p.Loops() }), + cmp.Transformer("loop", func(l *s2.Loop) []s2.Point { return l.Vertices() }), + cmpopts.EquateApprox(0.00000001, 0), + cmpopts.EquateEmpty(), + } +} + +func TestFenceposts(t *testing.T) { + // This is a test for a fictional function, Fenceposts, which draws a fence + // around some Place object. The details are not important, except that + // the result is some object that has s2 geometry (github.com/golang/geo/s2) + got := Fencepost(tomsDiner, 1*meter) + if diff := cmp.Diff(want, got, polygonCmp()); diff != "" { + t.Errorf("Fencepost(tomsDiner, 1m) returned unexpected diff (-want+got):\n%v", diff) + } +} + +func FuzzFencepost(f *testing.F) { + // Fuzz test (https://go.dev/doc/fuzz) for the same. + + f.Add(tomsDiner, 1*meter) + f.Add(school, 3*meter) + + f.Fuzz(func(t *testing.T, geo Place, padding Length) { + got := Fencepost(geo, padding) + // Simple reference implementation: not used in prod, but easy to + // reason about and therefore useful to check against in random tests. + reference := slowFencepost(geo, padding) + + // In the fuzz test, inputs and outputs can be large so don't + // bother with printing a diff. cmp.Equal is enough. + if !cmp.Equal(got, reference, polygonCmp()) { + t.Errorf("Fencepost returned wrong placement") + } + }) +} + + +The polygonCmp function is agnostic about how it’s called; it doesn’t take a concrete input type nor does it police what to do in case two objects don’t match. Therefore, more callers can make use of it. + +Note: There is an analogy between test helpers and plain library code. Code in libraries should usually not panic except in rare circumstances; code called from a test should not stop the test unless there is no point in proceeding. + +Designing extensible validation APIs + +Most of the advice about testing in the style guide is about testing your own code. This section is about how to provide facilities for other people to test the code they write to ensure that it conforms to your library’s requirements. + +Acceptance testing + +Such testing is referred to as acceptance testing. The premise of this kind of testing is that the person using the test does not know every last detail of what goes on in the test; they just hand the inputs over to the testing facility to do the work. This can be thought of as a form of inversion of control. + +In a typical Go test, the test function controls the program flow, and the no assert and test functions guidance encourages you to keep it that way. This section explains how to author support for these tests in a way that is consistent with Go style. + +Before diving into how, consider an example from io/fs, excerpted below: + +type FS interface { + Open(name string) (File, error) +} + + +While there exist well-known implementations of fs.FS, a Go developer may be expected to author one. To help validate the user-implemented fs.FS is correct, a generic library has been provided in testing/fstest called fstest.TestFS. This API treats the implementation as a blackbox to make sure it upholds the most basic parts of the io/fs contract. + +Writing an acceptance test + +Now that we know what an acceptance test is and why you might use one, let’s explore building an acceptance test for package chess, a package used to simulate chess games. Users of chess are expected to implement the chess.Player interface. These implementations are the primary thing we will validate. Our acceptance test concerns itself with whether the player implementation makes legal moves, not whether the moves are smart. + +Create a new package for the validation behavior, customarily named by appending the word test to the package name (for example, chesstest). + +Create the function that performs the validation by accepting the implementation under test as an argument and exercises it: + +// ExercisePlayer tests a Player implementation in a single turn on a board. +// The board itself is spot checked for sensibility and correctness. +// +// It returns a nil error if the player makes a correct move in the context +// of the provided board. Otherwise ExercisePlayer returns one of this +// package's errors to indicate how and why the player failed the +// validation. +func ExercisePlayer(b *chess.Board, p chess.Player) error + + +The test should note which invariants are broken and how. Your design can choose between two disciplines for failure reporting: + +Fail fast: return an error as soon as the implementation violates an invariant. + +This is the simplest approach, and it works well if the acceptance test is expected to execute quickly. Simple error sentinels and custom types can be used easily here, which conversely makes testing the acceptance test easy. + +for color, army := range b.Armies { + // The king should never leave the board, because the game ends at + // checkmate. + if army.King == nil { + return &MissingPieceError{Color: color, Piece: chess.King} + } +} + + +Aggregate all failures: collect all failures, and report them all. + +This approach resembles the keep going guidance in feel and may be preferable if the acceptance test is expected to execute slowly. + +How you aggregate the failures should be dictated by whether you want to give users the ability or yourself the ability to interrogate individual failures (for example, for you to test your acceptance test). Below demonstrates using a custom error type that aggregates errors: + +var badMoves []error + +move := p.Move() +if putsOwnKingIntoCheck(b, move) { + badMoves = append(badMoves, PutsSelfIntoCheckError{Move: move}) +} + +if len(badMoves) > 0 { + return SimulationError{BadMoves: badMoves} +} +return nil + + +The acceptance test should honor the keep going guidance by not calling t.Fatal unless the test detects a broken invariant in the system being exercised. + +For example, t.Fatal should be reserved for exceptional cases such as setup failure as usual: + +func ExerciseGame(t *testing.T, cfg *Config, p chess.Player) error { + t.Helper() + + if cfg.Simulation == Modem { + conn, err := modempool.Allocate() + if err != nil { + t.Fatalf("No modem for the opponent could be provisioned: %v", err) + } + t.Cleanup(func() { modempool.Return(conn) }) + } + // Run acceptance test (a whole game). +} + + +This technique can help you create concise, canonical validations. But do not attempt to use it to bypass the guidance on assertions. + +The final product should be in a form similar to this for end users: + +// Good: +package deepblue_test + +import ( + "chesstest" + "deepblue" +) + +func TestAcceptance(t *testing.T) { + player := deepblue.New() + err := chesstest.ExerciseGame(t, chesstest.SimpleGame, player) + if err != nil { + t.Errorf("Deep Blue player failed acceptance test: %v", err) + } +} + + +Use real transports + +When testing component integrations, especially where HTTP or RPC are used as the underlying transport between the components, prefer using the real underlying transport to connect to the test version of the backend. + +For example, suppose the code you want to test (sometimes referred to as “system under test” or SUT) interacts with a backend that implements the long running operations API. To test your SUT, use a real OperationsClient that is connected to a test double (e.g., a mock, stub, or fake) of the OperationsServer. + +This is recommended over hand-implementing the client, due to the complexity of imitating client behavior correctly. By using the production client with a test-specific server, you ensure your test is using as much of the real code as possible. + +Tip: Where possible, use a testing library provided by the authors of the service under test. + +t.Error vs. t.Fatal + +As discussed in decisions, tests should generally not abort at the first encountered problem. + +However, some situations require that the test not proceed. Calling t.Fatal is appropriate when some piece of test setup fails, especially in test setup helpers, without which you cannot run the rest of the test. In a table-driven test, t.Fatal is appropriate for failures that set up the whole test function before the test loop. Failures that affect a single entry in the test table, which make it impossible to continue with that entry, should be reported as follows: + +If you’re not using t.Run subtests, use t.Error followed by a continue statement to move on to the next table entry. +If you’re using subtests (and you’re inside a call to t.Run), use t.Fatal, which ends the current subtest and allows your test case to progress to the next subtest. + +Warning: It is not always safe to call t.Fatal and similar functions. More details here. + +Error handling in test helpers + +Note: This section discusses test helpers in the sense Go uses the term: functions that perform test setup and cleanup, not common assertion facilities. See the test functions section for more discussion. + +Operations performed by a test helper sometimes fail. For example, setting up a directory with files involves I/O, which can fail. When test helpers fail, their failure often signifies that the test cannot continue, since a setup precondition failed. When this happens, prefer calling one of the Fatal functions in the helper: + +// Good: +func mustAddGameAssets(t *testing.T, dir string) { + t.Helper() + if err := os.WriteFile(path.Join(dir, "pak0.pak"), pak0, 0644); err != nil { + t.Fatalf("Setup failed: could not write pak0 asset: %v", err) + } + if err := os.WriteFile(path.Join(dir, "pak1.pak"), pak1, 0644); err != nil { + t.Fatalf("Setup failed: could not write pak1 asset: %v", err) + } +} + + +This keeps the calling side cleaner than if the helper were to return the error to the test itself: + +// Bad: +func addGameAssets(t *testing.T, dir string) error { + t.Helper() + if err := os.WriteFile(path.Join(d, "pak0.pak"), pak0, 0644); err != nil { + return err + } + if err := os.WriteFile(path.Join(d, "pak1.pak"), pak1, 0644); err != nil { + return err + } + return nil +} + + +Warning: It is not always safe to call t.Fatal and similar functions. More details here. + +The failure message should include a description of what happened. This is important, as you may be providing a testing API to many users, especially as the number of error-producing steps in the helper increases. When the test fails, the user should know where, and why. + +Tip: Go 1.14 introduced a t.Cleanup function that can be used to register cleanup functions that run when your test completes. The function also works with test helpers. See GoTip #4: Cleaning Up Your Tests for guidance on simplifying test helpers. + +The snippet below in a fictional file called paint_test.go demonstrates how (*testing.T).Helper influences failure reporting in a Go test: + +package paint_test + +import ( + "fmt" + "testing" +) + +func paint(color string) error { + return fmt.Errorf("no %q paint today", color) +} + +func badSetup(t *testing.T) { + // This should call t.Helper, but doesn't. + if err := paint("taupe"); err != nil { + t.Fatalf("Could not paint the house under test: %v", err) // line 15 + } +} + +func goodSetup(t *testing.T) { + t.Helper() + if err := paint("lilac"); err != nil { + t.Fatalf("Could not paint the house under test: %v", err) + } +} + +func TestBad(t *testing.T) { + badSetup(t) + // ... +} + +func TestGood(t *testing.T) { + goodSetup(t) // line 32 + // ... +} + + +Here is an example of this output when run. Note the highlighted text and how it differs: + +=== RUN TestBad + paint_test.go:15: Could not paint the house under test: no "taupe" paint today +--- FAIL: TestBad (0.00s) +=== RUN TestGood + paint_test.go:32: Could not paint the house under test: no "lilac" paint today +--- FAIL: TestGood (0.00s) +FAIL + + +The error with paint_test.go:15 refers to the line of the setup function that failed in badSetup: + +t.Fatalf("Could not paint the house under test: %v", err) + +Whereas paint_test.go:32 refers to the line of the test that failed in TestGood: + +goodSetup(t) + +Correctly using (*testing.T).Helper attributes the location of the failure much better when: + +the helper functions grow +the helper functions call other helpers +the amount of helper usage in the test functions grow + +Tip: If a helper calls (*testing.T).Error or (*testing.T).Fatal, provide some context in the format string to help determine what went wrong and why. + +Tip: If nothing a helper does can cause a test to fail, it doesn’t need to call t.Helper. Simplify its signature by removing t from the function parameter list. + +Don’t call t.Fatal from separate goroutines + +As documented in package testing, it is incorrect to call t.FailNow, t.Fatal, etc. from any goroutine but the one running the Test function (or the subtest). If your test starts new goroutines, they must not call these functions from inside these goroutines. + +Test helpers usually don’t signal failure from new goroutines, and therefore it is all right for them to use t.Fatal. If in doubt, call t.Error and return instead. + +// Good: +func TestRevEngine(t *testing.T) { + engine, err := Start() + if err != nil { + t.Fatalf("Engine failed to start: %v", err) + } + + num := 11 + var wg sync.WaitGroup + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + defer wg.Done() + if err := engine.Vroom(); err != nil { + // This cannot be t.Fatalf. + t.Errorf("No vroom left on engine: %v", err) + return + } + if rpm := engine.Tachometer(); rpm > 1e6 { + t.Errorf("Inconceivable engine rate: %d", rpm) + } + }() + } + wg.Wait() + + if seen := engine.NumVrooms(); seen != num { + t.Errorf("engine.NumVrooms() = %d, want %d", seen, num) + } +} + + +Adding t.Parallel to a test or subtest does not make it unsafe to call t.Fatal. + +When all calls to the testing API are in the test function, it is usually easy to spot incorrect usage because the go keyword is plain to see. Passing testing.T arguments around makes tracking such usage harder. Typically, the reason for passing these arguments is to introduce a test helper, and those should not depend on the system under test. Therefore, if a test helper registers a fatal test failure, it can and should do so from the test’s goroutine. + +Use field names in struct literals + +In table-driven tests, prefer to specify field names when initializing test case struct literals. This is helpful when the test cases cover a large amount of vertical space (e.g. more than 20-30 lines), when there are adjacent fields with the same type, and also when you wish to omit fields which have the zero value. For example: + +// Good: +func TestStrJoin(t *testing.T) { + tests := []struct { + slice []string + separator string + skipEmpty bool + want string + }{ + { + slice: []string{"a", "b", ""}, + separator: ",", + want: "a,b,", + }, + { + slice: []string{"a", "b", ""}, + separator: ",", + skipEmpty: true, + want: "a,b", + }, + // ... + } + // ... +} + + +Keep setup code scoped to specific tests + +Where possible, setup of resources and dependencies should be as closely scoped to specific test cases as possible. For example, given a setup function: + +// mustLoadDataSet loads a data set for the tests. +// +// This example is very simple and easy to read. Often realistic setup is more +// complex, error-prone, and potentially slow. +func mustLoadDataset(t *testing.T) []byte { + t.Helper() + data, err := os.ReadFile("path/to/your/project/testdata/dataset") + + if err != nil { + t.Fatalf("Could not load dataset: %v", err) + } + return data +} + + +Call mustLoadDataset explicitly in test functions that need it: + +// Good: +func TestParseData(t *testing.T) { + data := mustLoadDataset(t) + parsed, err := ParseData(data) + if err != nil { + t.Fatalf("Unexpected error parsing data: %v", err) + } + want := &DataTable{ /* ... */ } + if got := parsed; !cmp.Equal(got, want) { + t.Errorf("ParseData(data) = %v, want %v", got, want) + } +} + +func TestListContents(t *testing.T) { + data := mustLoadDataset(t) + contents, err := ListContents(data) + if err != nil { + t.Fatalf("Unexpected error listing contents: %v", err) + } + want := []string{ /* ... */ } + if got := contents; !cmp.Equal(got, want) { + t.Errorf("ListContents(data) = %v, want %v", got, want) + } +} + +func TestRegression682831(t *testing.T) { + if got, want := guessOS("zpc79.example.com"), "grhat"; got != want { + t.Errorf(`guessOS("zpc79.example.com") = %q, want %q`, got, want) + } +} + + +The test function TestRegression682831 does not use the data set and therefore does not call mustLoadDataset, which could be slow and failure-prone: + +// Bad: +var dataset []byte + +func TestParseData(t *testing.T) { + // As documented above without calling mustLoadDataset directly. +} + +func TestListContents(t *testing.T) { + // As documented above without calling mustLoadDataset directly. +} + +func TestRegression682831(t *testing.T) { + if got, want := guessOS("zpc79.example.com"), "grhat"; got != want { + t.Errorf(`guessOS("zpc79.example.com") = %q, want %q`, got, want) + } +} + +func init() { + dataset = mustLoadDataset() +} + + +A user may wish to run a function in isolation of the others and should not be penalized by these factors: + +# No reason for this to perform the expensive initialization. +$ go test -run TestRegression682831 + + +When to use a custom TestMain entrypoint + +If all tests in the package require common setup and the setup requires teardown, you can use a custom testmain entrypoint. This can happen if the resource the test cases require is especially expensive to setup, and the cost should be amortized. Typically you have extracted any unrelated tests from the test suite at that point. It is typically only used for functional tests. + +Using a custom TestMain should not be your first choice due the amount of care that should be taken for correct use. Consider first whether the solution in the amortizing common test setup section or an ordinary test helper is sufficient for your needs. + +// Good: +var db *sql.DB + +func TestInsert(t *testing.T) { /* omitted */ } + +func TestSelect(t *testing.T) { /* omitted */ } + +func TestUpdate(t *testing.T) { /* omitted */ } + +func TestDelete(t *testing.T) { /* omitted */ } + +// runMain sets up the test dependencies and eventually executes the tests. +// It is defined as a separate function to enable the setup stages to clearly +// defer their teardown steps. +func runMain(ctx context.Context, m *testing.M) (code int, err error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + d, err := setupDatabase(ctx) + if err != nil { + return 0, err + } + defer d.Close() // Expressly clean up database. + db = d // db is defined as a package-level variable. + + // m.Run() executes the regular, user-defined test functions. + // Any defer statements that have been made will be run after m.Run() + // completes. + return m.Run(), nil +} + +func TestMain(m *testing.M) { + code, err := runMain(context.Background(), m) + if err != nil { + // Failure messages should be written to STDERR, which log.Fatal uses. + log.Fatal(err) + } + // NOTE: defer statements do not run past here due to os.Exit + // terminating the process. + os.Exit(code) +} + + +Ideally a test case is hermetic between invocations of itself and between other test cases. + +At the very least, ensure that individual test cases reset any global state they have modified if they have done so (for instance, if the tests are working with an external database). + +Amortizing common test setup + +Using a sync.Once may be appropriate, though not required, if all of the following are true about the common setup: + +It is expensive. +It only applies to some tests. +It does not require teardown. +// Good: +var dataset struct { + once sync.Once + data []byte + err error +} + +func mustLoadDataset(t *testing.T) []byte { + t.Helper() + dataset.once.Do(func() { + data, err := os.ReadFile("path/to/your/project/testdata/dataset") + // dataset is defined as a package-level variable. + dataset.data = data + dataset.err = err + }) + if err := dataset.err; err != nil { + t.Fatalf("Could not load dataset: %v", err) + } + return dataset.data +} + + +When mustLoadDataset is used in multiple test functions, its cost is amortized: + +// Good: +func TestParseData(t *testing.T) { + data := mustLoadDataset(t) + + // As documented above. +} + +func TestListContents(t *testing.T) { + data := mustLoadDataset(t) + + // As documented above. +} + +func TestRegression682831(t *testing.T) { + if got, want := guessOS("zpc79.example.com"), "grhat"; got != want { + t.Errorf(`guessOS("zpc79.example.com") = %q, want %q`, got, want) + } +} + + +The reason that common teardown is tricky is there is no uniform place to register cleanup routines. If the setup function (in this case mustLoadDataset) relies on a context, sync.Once may be problematic. This is because the second of two racing calls to the setup function would need to wait for the first call to finish before returning. This period of waiting cannot be easily made to respect the context’s cancellation. + +String concatenation + +There are several ways to concatenate strings in Go. Some examples include: + +The “+” operator +fmt.Sprintf +strings.Builder +text/template +safehtml/template + +Though there is no one-size-fits-all rule for which to choose, the following guidance outlines when each method is preferred. + +Prefer “+” for simple cases + +Prefer using “+” when concatenating few strings. This method is syntactically the simplest and requires no import. + +// Good: +key := "projectid: " + p + + +Prefer fmt.Sprintf when formatting + +Prefer using fmt.Sprintf when building a complex string with formatting. Using many “+” operators may obscure the end result. + +// Good: +str := fmt.Sprintf("%s [%s:%d]-> %s", src, qos, mtu, dst) + +// Bad: +bad := src.String() + " [" + qos.String() + ":" + strconv.Itoa(mtu) + "]-> " + dst.String() + + +Best Practice: When the output of the string-building operation is an io.Writer, don’t construct a temporary string with fmt.Sprintf just to send it to the Writer. Instead, use fmt.Fprintf to emit to the Writer directly. + +When the formatting is even more complex, prefer text/template or safehtml/template as appropriate. + +Prefer strings.Builder for constructing a string piecemeal + +Prefer using strings.Builder when building a string bit-by-bit. strings.Builder takes amortized linear time, whereas “+” and fmt.Sprintf take quadratic time when called sequentially to form a larger string. + +// Good: +b := new(strings.Builder) +for i, d := range digitsOfPi { + fmt.Fprintf(b, "the %d digit of pi is: %d\n", i, d) +} +str := b.String() + + +Note: For more discussion, see GoTip #29: Building Strings Efficiently. + +Constant strings + +Prefer to use backticks (`) when constructing constant, multi-line string literals. + +// Good: +usage := `Usage: + +custom_tool [args]` + +// Bad: +usage := "" + + "Usage:\n" + + "\n" + + "custom_tool [args]" + + +Global state + +Libraries should not force their clients to use APIs that rely on global state. They are advised not to expose APIs or export package level variables that control behavior for all clients as parts of their API. The rest of the section uses “global” and “package level state” synonymously. + +Instead, if your functionality maintains state, allow your clients to create and use instance values. + +Important: While this guidance is applicable to all developers, it is most critical for infrastructure providers who offer libraries, integrations, and services to other teams. + +// Good: +// Package sidecar manages subprocesses that provide features for applications. +package sidecar + +type Registry struct { plugins map[string]*Plugin } + +func New() *Registry { return &Registry{plugins: make(map[string]*Plugin)} } + +func (r *Registry) Register(name string, p *Plugin) error { ... } + + +Your users will instantiate the data they need (a *sidecar.Registry) and then pass it as an explicit dependency: + +// Good: +package main + +func main() { + sidecars := sidecar.New() + if err := sidecars.Register("Cloud Logger", cloudlogger.New()); err != nil { + log.Exitf("Could not setup cloud logger: %v", err) + } + cfg := &myapp.Config{Sidecars: sidecars} + myapp.Run(context.Background(), cfg) +} + + +There are different approaches to migrating existing code to support dependency passing. The main one you will use is passing dependencies as parameters to constructors, functions, methods, or struct fields on the call chain. + +See also: + +Go Tip #5: Slimming Your Client Libraries +Go Tip #24: Use Case-Specific Constructions +Go Tip #40: Improving Time Testability with Function Parameters +Go Tip #41: Identify Function Call Parameters +Go Tip #44: Improving Time Testability with Struct Fields +Go Tip #80: Dependency Injection Principles + +APIs that do not support explicit dependency passing become fragile as the number of clients increases: + +// Bad: +package sidecar + +var registry = make(map[string]*Plugin) + +func Register(name string, p *Plugin) error { /* registers plugin in registry */ } + + +Consider what happens in the case of tests exercising code that transitively relies on a sidecar for cloud logging. + +// Bad: +package app + +import ( + "cloudlogger" + "sidecar" + "testing" +) + +func TestEndToEnd(t *testing.T) { + // The system under test (SUT) relies on a sidecar for a production cloud + // logger already being registered. + ... // Exercise SUT and check invariants. +} + +func TestRegression_NetworkUnavailability(t *testing.T) { + // We had an outage because of a network partition that rendered the cloud + // logger inoperative, so we added a regression test to exercise the SUT with + // a test double that simulates network unavailability with the logger. + sidecar.Register("cloudlogger", cloudloggertest.UnavailableLogger) + ... // Exercise SUT and check invariants. +} + +func TestRegression_InvalidUser(t *testing.T) { + // The system under test (SUT) relies on a sidecar for a production cloud + // logger already being registered. + // + // Oops. cloudloggertest.UnavailableLogger is still registered from the + // previous test. + ... // Exercise SUT and check invariants. +} + + +Go tests are executed sequentially by default, so the tests above run as: + +TestEndToEnd +TestRegression_NetworkUnavailability, which overrides the default value of cloudlogger +TestRegression_InvalidUser, which requires the default value of cloudlogger registered in package sidecar + +This creates an order-dependent test case, which breaks running with test filters, and prevents tests from running in parallel or being sharded. + +Using global state poses problems that lack easy answers for you and the API’s clients: + +What happens if a client needs to use different and separately operating sets of Plugins (for example, to support multiple servers) in the same process space? + +What happens if a client wants to replace a registered Plugin with an alternative implementation in a test, like a test double? + +What happens if a client’s tests require hermeticity between instances of a Plugin, or between all of the plugins registered? + +What happens if multiple clients Register a Plugin under the same name? Which one wins, if any? + +How should errors be handled? If the code panics or calls log.Fatal, will that always be appropriate for all places in which API would be called? Can a client verify it doesn’t do something bad before doing so? + +Are there certain stages in a program’s startup phases or lifetime during which Register can be called and when it can’t? + +What happens if Register is called at the wrong time? A client could call Register in func init, before flags are parsed, or after main. The stage at which a function is called affects error handling. If the author of an API assumes the API is only called during program initialization without the requirement that it is, the assumption may nudge the author to design error handling to abort the program by modeling the API as a Must-like function. Aborting is not appropriate for general-purpose library functions that can be used at any stage. + +What if the client’s and the designer’s concurrency needs are mismatched? + +See also: + +Go Tip #36: Enclosing Package-Level State +Go Tip #71: Reducing Parallel Test Flakiness +Go Tip #80: Dependency Injection Principles +Error Handling: Look Before You Leap versus Easier to Ask for Forgiveness than Permission +Unit Testing Practices on Public APIs + +Global state has cascading effects on the health of the Google codebase. Global state should be approached with extreme scrutiny. + +Global state comes in several forms, and you can use a few litmus tests to identify when it is safe. + +Major forms of package state APIs + +Several of the most common problematic API forms are enumerated below: + +Top-level variables irrespective of whether they are exported. + +// Bad: +package logger + +// Sinks manages the default output sources for this package's logging API. This +// variable should be set at package initialization time and never thereafter. +var Sinks []Sink + + +See the litmus tests to know when these are safe. + +The service locator pattern. See the first example. The service locator pattern itself is not problematic, rather the locator being defined as global. + +Registries for callbacks and similar behaviors. + +// Bad: +package health + +var unhealthyFuncs []func + +func OnUnhealthy(f func()) { + unhealthyFuncs = append(unhealthyFuncs, f) +} + + +Thick-Client singletons for things like backends, storage, data access layers, and other system resources. These often pose additional problems with service reliability. + +// Bad: +package useradmin + +var client pb.UserAdminServiceClientInterface + +func Client() *pb.UserAdminServiceClient { + if client == nil { + client = ... // Set up client. + } + return client +} + + +Note: Many legacy APIs in the Google codebase do not follow this guidance; in fact, some Go standard libraries allow for configuration via global values. Nevertheless, the legacy API’s contravention of this guidance should not be used as precedent for continuing the pattern. + +It is better to invest in proper API design today than pay for redesigning later. + +Litmus tests + +APIs using the patterns above are unsafe when: + +Multiple functions interact via global state when executed in the same program, despite being otherwise independent (for example, authored by different authors in vastly different directories). +Independent test cases interact with each other through global state. +Users of the API are tempted to swap or replace global state for testing purposes, particularly to replace any part of the state with a test double, like a stub, fake, spy, or mock. +Users have to consider special ordering requirements when interacting with global state: func init, whether flags are parsed yet, etc. + +Provided the conditions above are avoided, there are a few limited circumstances under which these APIs are safe, namely when any of the following is true: + +The global state is logically constant (example). +The package’s observable behavior is stateless. For example, a public function may use a private global variable as a cache, but so long as the caller can’t distinguish cache hits from misses, the function is stateless. +The global state does not bleed into things that are external to the program, like sidecar processes or files on a shared filesystem. +There is no expectation of predictable behavior (example). + +Note: Sidecar processes may not strictly be process-local. They can and often are shared with more than one application process. Moreover, these sidecars often interact with external distributed systems. + +Further, the same stateless, idempotent, and local rules in addition to the base considerations above would apply to the code of the sidecar process itself! + +An example of one of these safe situations is package image with its image.RegisterFormat function. Consider the litmus tests from above applied to a typical decoder, like the one for handling the PNG format: + +Multiple calls to package image’s APIs that use the registered decoders (for example, image.Decode) cannot interfere with one another, similarly for tests. The only exception is image.RegisterFormat, but that is mitigated by the points below. +It is extremely unlikely that a user would want to replace a decoder with a test double, as the PNG decoder exemplifies a case in which our codebase’s preference for real objects applies. However, a user would be more likely to replace a decoder with a test double if the decoder statefully interacted with operating system resources (for example, the network). +Collisions in registration are conceivable, though they are probably rare in practice. +The decoders are stateless, idempotent, and pure. + +Providing a default instance + +While not recommended, it is acceptable to provide a simplified API that uses package level state if you need to maximize convenience for the user. + +Follow the litmus tests with these guidelines in such cases: + +The package must offer clients the ability to create isolated instances of package types as described above. +The public APIs that use global state must be a thin proxy to the previous API. A good example of this is http.Handle internally calling (*http.ServeMux).Handle on the package variable http.DefaultServeMux. + +This package-level API must only be used by binary build targets, not libraries, unless the libraries are undertaking a refactoring to support dependency passing. Infrastructure libraries that can be imported by other packages must not rely on package-level state of the packages they import. + +For example, an infrastructure provider implementing a sidecar that is to be shared with other teams using the API from the top should offer an API to accommodate this: + +// Good: +package cloudlogger + +func New() *Logger { ... } + +func Register(r *sidecar.Registry, l *Logger) { + r.Register("Cloud Logging", l) +} + +This package-level API must document and enforce its invariants (for example, at which stage in the program’s life it can be called, whether it can be used concurrently). Further, it must provide an API to reset global state to a known-good default (for example, to facilitate testing). + +See also: + +Go Tip #36: Enclosing Package-Level State +Go Tip #80: Dependency Injection Principles + +Interfaces + +Interfaces in Go are powerful but can be overused or misunderstood. Because Go interfaces are satisfied implicitly, they are a structural tool rather than a declarative one. The following guidance provides the best practices for how to design and return interfaces in Go without over-engineering your codebase. + +Refer to Decisions’ section on interfaces for a summary. + +Avoid unnecessary interfaces + +The most common mistake is creating an interface before a real need exists. + +Don’t confuse the concept with the keyword: Just because you are designing a “service” or a “repository” or similar pattern doesn’t mean you need a named interface type (e.g., type Service interface). Focus on the behavior and its concrete implementation first. + +Reuse existing interfaces: If an interface already exists, especially in generated code, like a RPC client or server, use it (testing RPC). Do not wrap a generated RPC code in a new, manual interface just for the sake of abstraction or testing. Use real transports instead. + +Don’t define back doors only for tests: Do not export a test double implementation of an interface from an API that consumes it. Instead, prefer to design the API so that it can be tested using the public API of the real implementation. + +Every exported type increases the cognitive load for the reader. When you export a test double alongside the real implementation, you force the reader to understand three entities (the interface, the real implementation, and the test double) instead of one. + +Export an interface for a test double when you have a material need to support substitution. + +When it does make sense to create an interface: + +Multiple implementations: When there are two or more concrete types that must be handled by the same logic (e.g., something that operates with both json.Encoder and gob.GobEncoder), the API consumer could define an interface. + +Decoupling packages: To break circular dependencies between two packages (see an example), an API producer could define an interface. + +Caution: Carefully observe guidance on Package Size. Introducing interfaces to break dependency cycles is often a signal of improperly structured packages. + +Hiding complexity: When a concrete type has a massive API surface, but a specific function only needs one or two methods, an API consumer may define an interface. + +Interface ownership and visibility + +Do not export interface types unnecessarily: If an interface is only used internally within a package to satisfy a specific logic flow, keep the interface unexported. Exporting an interface commits you to maintaining that API for external callers. + +The consumer defines the interface: In Go, interfaces generally belong in the package that uses them, not the package that implements them. The consumer should define only the methods they actually use GoTip #78: Minimal Viable Interfaces, adhering to the idea that the bigger the interface, the weaker the abstraction. + +There are common scenarios where it often makes sense for the producer (the package providing the logic) to export the interface: + +The interface is the product: When a package’s primary purpose is to provide a common protocol that many different implementations must follow, the producer defines the interface. For example, io.Writer, hash.Hash. The concept of “protocol” includes aspects like documentation about critical behaviors (e.g., expected use case, edge cases, concurrency) that need to be centrally and canonically explicated. Another prominent example of this is generated interfaces from protobuf. It doesn’t abstract a specific behavior, it defines a boundary. Its purpose is to ensure that your server implementation exactly matches the schema defined in the .proto file. Here, the interface serves as a rigid legal contract between the service and its clients. + +For large systems, if the interface lives inside a huge implementation package, every client is forced to import the entire world just to reference the interface. You may define the interface in a standalone, implementation-free package, avoiding unnecessary symbols and potential circular dependencies. This is also the same philosophy used by generated code from protobuf. + +Prevent interface bloat: In large codebases, maintenance becomes difficult if numerous packages utilize the same AuthService while each defining an identical type Authorizer interface. While Go often favors a little copying over a little dependency, keep in mind that maintaining perfectly mirrored interfaces (see point above) across many packages can create an unnecessary burden. + +Resolve circular dependency: see an example below. + +Designing effective interfaces + +Keep interfaces small: The larger the interface, the harder it is to implement and to write code that takes advantage of it. Small interfaces are easier to compose into larger ones if needed. + +Documentation: Treat every interface as the “user manual” for your abstraction. The depth of your documentation should be proportional to the interface’s cognitive load, not just the count of its methods. Whether an interface has ten methods or a single Write of io.Writer, if a programmer is expected to interact with that type, the API must be documented thoroughly. + +Single-method interfaces: documentation on the type itself is usually sufficient (e.g., io.Writer). Explain its contract, edge cases, and expected errors. +Multi-method interfaces: each individual method requires its own documentation. +Unexported interfaces: consider documenting them anyway. They are often the glue that holds complex internal logic together, and because they are invisible to external users, they can easily become mystery code for future maintainers (including your future self). + +Accept interfaces, return concrete types: Returning a concrete type allows the caller to use the full functionality of the value without being locked into a specific interface abstraction GoTip #49: Accept Interfaces, Return Concrete Types. + +There are several common scenarios where returning an interface is the idiomatic choice: + +Encapsulation: While interfaces cannot strictly hide exported methods (as they remain accessible via type assertions), returning an interface is a powerful tool for limiting the default API surface and guiding the caller’s behavior.. The most common example is the error interface; you almost never return a concrete error type like *MyCustomError. + +Consider a ThrottledReader that implements io.Reader but also has a Refill method for internal bucket management. Returning the concrete *ThrottledReader invites the caller to manage the bucket manually, which could lead to race conditions or broken rate-limiting logic. By returning an interface, you tell the caller that your only job is to consume this reader. If you try to cast this back to a ThrottledReader to Refill the internal bucket, you are breaking the contract. + +// Good: +type ThrottledReader struct { + source io.Reader + limit int // bytes per second + balance int // current allowance of bytes + lastRefill time.Time +} + +// Read implements the io.Reader interface with rate-limiting logic. +func (t *ThrottledReader) Read(p []byte) (int, error) { ... } + +// Refill manually adds tokens to the bucket. +// INTERNAL USE ONLY: Calling this from outside breaks the rate limit logic. +func (t *ThrottledReader) Refill(amount int) { + t.balance = min(t.balance + amount, t.limit) +} + +// New returns the io.Reader with rate-limiting. +func New(r io.Reader, bytesPerSec int) io.Reader { + return &ThrottledReader{ + source: r, + limit: bytesPerSec, + balance: bytesPerSec, // start with a full bucket + lastRefill: time.Now(), + } +} + + +This raises a natural question: if Refill is dangerous, why export it at all? In complex systems, you often need internal orchestration. For example, an AggregateReader manages multiple ThrottledReader values to ensure total bandwidth across all streams stays under a global limit. This coordinator needs to call Refill to distribute tokens, but the non-power user processing the data should never see that capability. + +Caution: Before returning an interface to hide implementation, ask: “Would a user calling these extra methods actually break the system’s integrity or meaningfully limit maintainability?” If the extra details allow the user to bypass safety checks, or if exposing the concrete type makes it impossible to change the underlying provider later without a breaking change, you may return an interface. Do not rotely encapsulate without reason. + +Certain patterns: If a function is designed to return one of several different concrete types based on decisions made at runtime, it must return an interface. This is commonly true with command, chaining, factory, and strategy patterns. Consider this code that selects which encoder to use based the requested format: + +// Good: +func NewWriter(format string) io.Writer { + switch format { + case "json": + return &jsonWriter{} + case "xml": + return &xmlWriter{} + default: + return &textWriter{} + } +} + + +The following example of a chaining API demonstrates how returning an interface enables polymorphic behavior. By allowing callers to use either client.Do(req) or client.WithAuth("token").Do(req), you can swap implementations without breaking the calling code. + +// Good: +type Client interface { + WithAuth(token string) Client + Do(req *Request) error +} + + +These patterns are guidelines, not rules. Avoid forcing an interface if a single, robust concrete type can handle the abstraction internally. For example, the standard database/sql library exports a single concrete DB type instead of forcing an interface to handle types like MySQLDB and OracleDB. + +Avoiding circular dependencies: If returning a concrete type would require importing a package that already imports your current package, you must return an interface to break the circular dependency. + +For example: + +// Bad: +package app + +import "myproject/plugin" + +type Config struct { + APIKey string +} + +func Start() { + p := plugin.New() +} + +// Bad: +package plugin + +import "myproject/app" // ERROR: Import cycle! + +func New() *app.Config { + return &app.Config{APIKey: "secret"} +} + + +In this case, plugin’s New cannot return *app.Config because it would create a circular import. To break this, we use the fact that interfaces are satisfied implicitly. We move the “contract” to a neutral place or have the producer return an interface that the consumer already understands. + +If plugin’s New returns an interface instead of the concrete *app.Config struct, it no longer needs to import package app. + +package plugin + +type Configurer interface { + APIKey() string +} + +type localConfig struct { + key string +} + +func (c localConfig) APIKey() string { return c.key } + +// New returns the interface Configurer instead of the concrete app.Config +func New() Configurer { + return &localConfig{key: "secret"} +} + +package app + +import "myproject/plugin" + +func Start() { + conf := plugin.New() // 'conf' is now a Configurer interface + fmt.Println(conf.APIKey()) +} + + +Caution: Carefully observe guidance on Package Size. Introducing interfaces to break dependency cycles is often a signal of improperly structured packages. Consolidated packages are often preferred over too many too small packages that fail to stand on their own. diff --git a/ai/skills/grill-me/SKILL.md b/ai/skills/grill-me/SKILL.md new file mode 100644 index 0000000..f19dd74 --- /dev/null +++ b/ai/skills/grill-me/SKILL.md @@ -0,0 +1,10 @@ +--- +name: grill-me +description: Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me". +--- + +Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer. + +Ask the questions one at a time. + +If a question can be answered by exploring the codebase, explore the codebase instead. \ No newline at end of file diff --git a/ai/skills/handoff/SKILL.md b/ai/skills/handoff/SKILL.md new file mode 100644 index 0000000..581ad9f --- /dev/null +++ b/ai/skills/handoff/SKILL.md @@ -0,0 +1,15 @@ +--- +name: handoff +description: Compact the current conversation into a handoff document for another agent to pick up. +argument-hint: "What will the next session be used for?" +--- + +Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace. + +Include a "suggested skills" section in the document, which suggests skills that the agent should invoke. + +Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead. + +Redact any sensitive information, such as API keys, passwords, or personally identifiable information. + +If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly. \ No newline at end of file diff --git a/ai/skills/html_css_expert/SKILL.md b/ai/skills/html_css_expert/SKILL.md new file mode 100644 index 0000000..0030167 --- /dev/null +++ b/ai/skills/html_css_expert/SKILL.md @@ -0,0 +1,15 @@ +--- +name: html_css_expert +description: Google HTML/CSS Style Guide Expert. Use this for all web frontend structure and styling. +--- +# HTML/CSS Expert + +Expert in Google HTML/CSS Style Guide. + +## Core Mandate +Strictly follow [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Principles +- Semantic HTML5. +- Efficient CSS with minimal nesting. +- Consistency in naming and formatting. diff --git a/ai/skills/html_css_expert/STYLE_GUIDE.md b/ai/skills/html_css_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..be6747e --- /dev/null +++ b/ai/skills/html_css_expert/STYLE_GUIDE.md @@ -0,0 +1,668 @@ +Google HTML/CSS Style Guide +Table of Contents +Background +General +General Style Rules +General Formatting Rules +General Meta Rules +HTML +HTML Style Rules +HTML Formatting Rules +CSS +CSS Style Rules +CSS Formatting Rules +CSS Meta Rules +Parting Words +Background + +This document defines formatting and style rules for HTML and CSS. It aims at improving collaboration, code quality, and enabling supporting infrastructure. It applies to raw, working files that use HTML and CSS, including Sass and GSS files. Tools are free to obfuscate, minify, and compile as long as the general code quality is maintained. + +General +General Style Rules +Protocol + +Use HTTPS for embedded resources where possible. + +Always use HTTPS (https:) for images and other media files, style sheets, and scripts, unless the respective files are not available over HTTPS. + + + + + + + + +/* Not recommended: omits the protocol */ +@import '//fonts.googleapis.com/css?family=Open+Sans'; + +/* Not recommended: uses HTTP */ +@import 'http://fonts.googleapis.com/css?family=Open+Sans'; +/* Recommended */ +@import 'https://fonts.googleapis.com/css?family=Open+Sans'; +General Formatting Rules +Indentation + +Indent by 2 spaces at a time. + +Don’t use tabs or mix tabs and spaces for indentation. + +
    +
  • Fantastic +
  • Great +
+.example { + color: blue; +} +Capitalization + +Use only lowercase. + +All code has to be lowercase: This applies to HTML element names, attributes, attribute values (unless text/CDATA), CSS selectors, properties, and property values (with the exception of strings). + + +Home + +Google +/* Not recommended */ +color: #E5E5E5; +/* Recommended */ +color: #e5e5e5; +Trailing Whitespace + +Remove trailing white spaces. + +Trailing white spaces are unnecessary and can complicate diffs. + + +

What?_ + +

Yes please. +General Meta Rules +Encoding + +Use UTF-8 (no BOM). + +Make sure your editor uses UTF-8 as character encoding, without a byte order mark. + +Specify the encoding in HTML templates and documents via . Do not specify the encoding of style sheets as these assume UTF-8. + +(More on encodings and when and how to specify them can be found in Handling character encodings in HTML and CSS.) + +Comments + +Explain code as needed, where possible. + +Use comments to explain code: What does it cover, what purpose does it serve, why is respective solution used or preferred? + +(This item is optional as it is not deemed a realistic expectation to always demand fully documented code. Mileage may vary heavily for HTML and CSS code and depends on the project’s complexity.) + +Action Items + +Mark todos and action items with TODO. + +Highlight todos by using the keyword TODO only, not other common formats like @@. + +Append action items after a colon as in TODO: action item. + +{# TODO: Revisit centering. #} +

Test
+ +
    +
  • Apples
  • +
  • Oranges
  • +
+HTML +HTML Style Rules +Document Type + +Use . + +Always put your HTML in no-quirks mode by including at the beginning of the document. + +A document without a doctype is rendered in “quirks mode”, and one with a different doctype may be rendered in “limited-quirks mode”. These modes don’t follow the widely-understood, widely-documented behavior for various core HTML and CSS constructs, and are likely to cause subtle failures and incompatibilities especially when re-using code that expects no-quirks mode. + +HTML Validity + +Use valid HTML where possible. + +Use valid HTML code unless that is not possible due to otherwise unattainable performance goals regarding file size. + +Use tools such as the W3C HTML validator to test. + +Using valid HTML is a measurable baseline quality attribute that contributes to learning about technical requirements and constraints, and that ensures proper HTML usage. + + +Test +
This is only a test. + + + +Test +
This is only a test.
+Semantics + +Use HTML according to its purpose. + +Use elements (sometimes incorrectly called “tags”) for what they have been created for. For example, use heading elements for headings, p elements for paragraphs, a elements for anchors, etc. + +Using HTML according to its purpose is important for accessibility, reuse, and code efficiency reasons. + + +
All recommendations
+ +All recommendations +Multimedia Fallback + +Provide alternative contents for multimedia. + +For multimedia, such as images, videos, animated objects via canvas, make sure to offer alternative access. For images that means use of meaningful alternative text (alt) and for video and audio transcripts and captions, if available. + +Providing alternative contents is important for accessibility reasons: A blind user has few cues to tell what an image is about without @alt, and other users may have no way of understanding what video or audio contents are about either. + +(For images whose alt attributes would introduce redundancy, and for images whose purpose is purely decorative which you cannot immediately use CSS for, use no alternative text, as in alt="".) + + + + +Spreadsheet screenshot. +Separation of Concerns + +Separate structure from presentation from behavior. + +Strictly keep structure (markup), presentation (styling), and behavior (scripting) apart, and try to keep the interaction between the three to an absolute minimum. + +That is, make sure documents and templates contain only HTML and HTML that is solely serving structural purposes. Move everything presentational into style sheets, and everything behavioral into scripts. + +In addition, keep the contact area as small as possible by linking as few style sheets and scripts as possible from documents and templates. + +Separating structure from presentation from behavior is important for maintenance reasons. It is always more expensive to change HTML documents and templates than it is to update style sheets and scripts. + + + +HTML sucks + + + +

HTML sucks

+

I’ve read about this on a few sites but now I’m sure: + HTML is stupid!!1 +

I can’t believe there’s no way to control the styling of + my website without doing everything all over again!
+ + +My first CSS-only redesign + +

My first CSS-only redesign

+

I’ve read about this on a few sites but today I’m actually + doing it: separating concerns and avoiding anything in the HTML of + my website that is presentational. +

It’s awesome! +Entity References + +Do not use entity references. + +There is no need to use entity references like —, ”, or ☺, assuming the same encoding (UTF-8) is used for files and editors as well as among teams. + +The only exceptions apply to characters with special meaning in HTML (like < and &) as well as control or “invisible” characters (like no-break spaces). + + +The currency symbol for the Euro is “&eur;”. + +The currency symbol for the Euro is “€”. +Optional Tags + +Omit optional tags (optional). + +For file size optimization and scannability purposes, consider omitting optional tags. The HTML5 specification defines what tags can be omitted. + +(This approach may require a grace period to be established as a wider guideline as it’s significantly different from what web developers are typically taught. For consistency and simplicity reasons it’s best served omitting all optional tags, not just a selection.) + + + + + + Spending money, spending bytes + + +

Sic.

+ + + + +Saving money, saving bytes +

Qed. +type Attributes + +Omit type attributes for style sheets and scripts. + +Do not use type attributes for style sheets (unless not using CSS) and scripts (unless not using JavaScript). + +Specifying type attributes in these contexts is not necessary as HTML5 implies text/css and text/javascript as defaults. This can be safely done even for older browsers. + + + + + + + + + +id Attributes + +Avoid unnecessary id attributes. + +Prefer class attributes for styling and data attributes for scripting. + +Where id attributes are strictly required, always include a hyphen in the value to ensure it does not match the JavaScript identifier syntax, e.g. use user-profile rather than just profile or userProfile. + +When an element has an id attribute, browsers will make that available as a named property on the global window prototype, which may cause unexpected behavior. While id attribute values containing a hyphen are still available as property names, these cannot be referenced as global JavaScript variables. + + +

+ +
+ … +
+ … +
+HTML Formatting Rules +General Formatting + +Use a new line for every block, list, or table element, and indent every such child element. + +Independent of the styling of an element (as CSS allows elements to assume a different role per display property), put every block, list, or table element on a new line. + +Also, indent them if they are child elements of a block, list, or table element. + +(If you run into issues around whitespace between list items it’s acceptable to put all li elements in one line. A linter is encouraged to throw a warning instead of an error.) + +
+

Space, the final frontier.

+
+
    +
  • Moe +
  • Larry +
  • Curly +
+ + + + + +
Income + Taxes +
$ 5.00 + $ 4.50 +
+HTML Line-Wrapping + +Break long lines (optional). + +While there is no column limit recommendation for HTML, you may consider wrapping long lines if it significantly improves readability. + +When line-wrapping, each continuation line should be indented to distinguish wrapped attributes from child elements. Lines should be wrapped consistently within a project, ideally enforced by automated code formatting tools. + + + + + +HTML Quotation Marks + +When quoting attributes values, use double quotation marks. + +Use double ("") rather than single quotation marks ('') around attribute values. + + +Sign in + +Sign in +CSS +CSS Style Rules +CSS Validity + +Use valid CSS where possible. + +Unless dealing with CSS validator bugs or requiring proprietary syntax, use valid CSS code. + +Use tools such as the W3C CSS validator to test. + +Using valid CSS is a measurable baseline quality attribute that allows to spot CSS code that may not have any effect and can be removed, and that ensures proper CSS usage. + +Class Naming + +Use meaningful or generic class names. + +Instead of presentational or cryptic names, always use class names that reflect the purpose of the element in question, or that are otherwise generic. + +Names that are specific and reflect the purpose of the element should be preferred as these are most understandable and the least likely to change. + +Generic names are simply a fallback for elements that have no particular or no meaning different from their siblings. They are typically needed as “helpers.” + +Using functional or generic names reduces the probability of unnecessary document or template changes. + +/* Not recommended: meaningless */ +.yee-1901 {} + +/* Not recommended: presentational */ +.button-green {} +.clear {} +/* Recommended: specific */ +.gallery {} +.login {} +.video {} + +/* Recommended: generic */ +.aux {} +.alt {} +Class Name Style + +Use class names that are as short as possible but as long as necessary. + +Try to convey what a class is about while being as brief as possible. + +Using class names this way contributes to acceptable levels of understandability and code efficiency. + +/* Not recommended */ +.navigation {} +.atr {} +/* Recommended */ +.nav {} +.author {} +Class Name Delimiters + +Separate words in class names by a hyphen. + +Do not concatenate words and abbreviations in selectors by any characters (including none at all) other than hyphens, in order to improve understanding and scannability. + +/* Not recommended: does not separate the words “demo” and “image” */ +.demoimage {} + +/* Not recommended: uses underscore instead of hyphen */ +.error_status {} +/* Recommended */ +.video-id {} +.ads-sample {} +Prefixes + +Prefix selectors with an application-specific prefix (optional). + +In large projects as well as for code that gets embedded in other projects or on external sites use prefixes (as namespaces) for class names. Use short, unique identifiers followed by a dash. + +Using namespaces helps preventing naming conflicts and can make maintenance easier, for example in search and replace operations. + +.adw-help {} /* AdWords */ +.maia-note {} /* Maia */ +Type Selectors + +Avoid qualifying class names with type selectors. + +Unless necessary (for example with helper classes), do not use element names in conjunction with classes. + +Avoiding unnecessary ancestor selectors is useful for performance reasons. + +/* Not recommended */ +ul.example {} +div.error {} +/* Recommended */ +.example {} +.error {} +ID Selectors + +Avoid ID selectors. + +ID attributes are expected to be unique across an entire page, which is difficult to guarantee when a page contains many components worked on by many different engineers. Class selectors should be preferred in all situations. + +/* Not recommended */ +#example {} +/* Recommended */ +.example {} +Shorthand Properties + +Use shorthand properties where possible. + +CSS offers a variety of shorthand properties (like font) that should be used whenever possible, even in cases where only one value is explicitly set. + +Using shorthand properties is useful for code efficiency and understandability. + +/* Not recommended */ +border-top-style: none; +font-family: palatino, georgia, serif; +font-size: 100%; +line-height: 1.6; +padding-bottom: 2em; +padding-left: 1em; +padding-right: 1em; +padding-top: 0; +/* Recommended */ +border-top: 0; +font: 100%/1.6 palatino, georgia, serif; +padding: 0 1em 2em; +0 and Units + +Omit unit specification after “0” values, unless required. + +Do not use units after 0 values unless they are required. + +flex: 0px; /* This flex-basis component requires a unit. */ +flex: 1 1 0px; /* Not ambiguous without the unit, but needed in IE11. */ +margin: 0; +padding: 0; +Leading 0s + +Always include leading “0”s in values. + +Put 0s in front of values or lengths between -1 and 1. + +font-size: 0.8em; +Hexadecimal Notation + +Use 3 character hexadecimal notation where possible. + +For color values that permit it, 3 character hexadecimal notation is shorter and more succinct. + +/* Not recommended */ +color: #eebbcc; +/* Recommended */ +color: #ebc; +Important Declarations + +Avoid using !important declarations. + +These declarations break the natural cascade of CSS and make it difficult to reason about and compose styles. Use selector specificity to override properties instead. + +/* Not recommended */ +.example { + font-weight: bold !important; +} +/* Recommended */ +.example { + font-weight: bold; +} +Hacks + +Avoid user agent detection as well as CSS “hacks”—try a different approach first. + +It’s tempting to address styling differences over user agent detection or special CSS filters, workarounds, and hacks. Both approaches should be considered last resort in order to achieve and maintain an efficient and manageable code base. Put another way, giving detection and hacks a free pass will hurt projects in the long run as projects tend to take the way of least resistance. That is, allowing and making it easy to use detection and hacks means using detection and hacks more frequently—and more frequently is too frequently. + +CSS Formatting Rules +Declaration Order + +Alphabetize declarations (optional). + +Sort declarations consistently within a project. In the absence of tooling to automate and enforce a consistent sort order, consider putting declarations in alphabetical order in order to achieve consistent code in a way that is easy to learn, remember, and manually maintain. + +Ignore vendor-specific prefixes for sorting purposes. However, multiple vendor-specific prefixes for a certain CSS property should be kept sorted (e.g. -moz prefix comes before -webkit). + +background: fuchsia; +border: 1px solid; +-moz-border-radius: 4px; +-webkit-border-radius: 4px; +border-radius: 4px; +color: black; +text-align: center; +text-indent: 2em; +Block Content Indentation + +Indent all block content. + +Indent all block content, that is rules within rules as well as declarations, so to reflect hierarchy and improve understanding. + +@media screen, projection { + + html { + background: #fff; + color: #444; + } + +} +Declaration Stops + +Use a semicolon after every declaration. + +End every declaration with a semicolon for consistency and extensibility reasons. + +/* Not recommended */ +.test { + display: block; + height: 100px +} +/* Recommended */ +.test { + display: block; + height: 100px; +} +Property Name Stops + +Use a space after a property name’s colon. + +Always use a single space between property and value (but no space between property and colon) for consistency reasons. + +/* Not recommended */ +h3 { + font-weight:bold; +} +/* Recommended */ +h3 { + font-weight: bold; +} +Declaration Block Separation + +Use a space between the last selector and the declaration block. + +Always use a single space between the last selector and the opening brace that begins the declaration block. + +The opening brace should be on the same line as the last selector in a given rule. + +/* Not recommended: missing space */ +.video{ + margin-top: 1em; +} + +/* Not recommended: unnecessary line break */ +.video +{ + margin-top: 1em; +} +/* Recommended */ +.video { + margin-top: 1em; +} +Selector and Declaration Separation + +Separate selectors and declarations by new lines. + +Always start a new line for each selector and declaration. + +/* Not recommended */ +a:focus, a:active { + position: relative; top: 1px; +} +/* Recommended */ +h1, +h2, +h3 { + font-weight: normal; + line-height: 1.2; +} +Rule Separation + +Separate rules by new lines. + +Always put a blank line (two line breaks) between rules. + +html { + background: #fff; +} + +body { + margin: auto; + width: 50%; +} +CSS Quotation Marks + +Use single ('') rather than double ("") quotation marks for attribute selectors and property values. + +Do not use quotation marks in URI values (url()). + +Exception: If you do need to use the @charset rule, use double quotation marks—single quotation marks are not permitted. + +/* Not recommended */ +@import url("https://www.google.com/css/maia.css"); + +html { + font-family: "open sans", arial, sans-serif; +} +/* Recommended */ +@import url(https://www.google.com/css/maia.css); + +html { + font-family: 'open sans', arial, sans-serif; +} +CSS Meta Rules +Section Comments + +Group sections by a section comment (optional). + +If possible, group style sheet sections together by using comments. Separate sections with new lines. + +/* Header */ + +.adw-header {} + +/* Footer */ + +.adw-footer {} + +/* Gallery */ + +.adw-gallery {} +Parting Words + +Be consistent. + +If you’re editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too. + +The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you’re saying rather than on how you’re saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this. diff --git a/ai/skills/java_expert/SKILL.md b/ai/skills/java_expert/SKILL.md new file mode 100644 index 0000000..7ac628c --- /dev/null +++ b/ai/skills/java_expert/SKILL.md @@ -0,0 +1,15 @@ +--- +name: java_expert +description: Google Java Style Guide Expert. Follows official Google Java coding standards. +--- +# Java Expert + +Expert in Google Java Style Guide. + +## Core Mandate +Strictly follow [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Principles +- Clean class hierarchies. +- Effective use of annotations and generics. +- Proper exception handling and logging. diff --git a/ai/skills/java_expert/STYLE_GUIDE.md b/ai/skills/java_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..455276a --- /dev/null +++ b/ai/skills/java_expert/STYLE_GUIDE.md @@ -0,0 +1,779 @@ +Google Java Style Guide +Table of Contents +1 Introduction +1.1 Terminology notes +1.2 Guide notes +2 Source file basics +2.1 File name +2.2 File encoding: UTF-8 +2.3 Special characters +3 Source file structure +3.1 License or copyright information, if present +3.2 Package declaration +3.3 Imports +3.4 Class declaration +3.5 Module declaration +4 Formatting +4.1 Braces +4.2 Block indentation: +2 spaces +4.3 One statement per line +4.4 Column limit: 100 +4.5 Line-wrapping +4.6 Whitespace +4.7 Grouping parentheses: recommended +4.8 Specific constructs +5 Naming +5.1 Rules common to all identifiers +5.2 Rules by identifier type +5.3 Camel case: defined +6 Programming Practices +6.1 @Override: always used +6.2 Caught exceptions: not ignored +6.3 Static members: qualified using class +6.4 Finalizers: not used +7 Javadoc +7.1 Formatting +7.2 The summary fragment +7.3 Where Javadoc is used +1 Introduction + +This document serves as the complete definition of Google's coding standards for source code in the Java™ Programming Language. A Java source file is described as being in Google Style if and only if it adheres to the rules herein. + +Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this document focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn't clearly enforceable (whether by human or tool). + +1.1 Terminology notes + +In this document, unless otherwise clarified: + +The term class is used inclusively to mean a normal class, record class, enum class, interface or annotation type (@interface). +The term member (of a class) is used inclusively to mean a nested class, field, method, or constructor; that is, all top-level contents of a class except initializers. +The term comment always refers to implementation comments. We do not use the phrase "documentation comments", and instead use the common term "Javadoc." + +Other "terminology notes" will appear occasionally throughout the document. + +1.2 Guide notes + +Example code in this document is non-normative. That is, while the examples are in Google Style, they may not illustrate the only stylish way to represent the code. Optional formatting choices made in examples should not be enforced as rules. + +2 Source file basics +2.1 File name + +For a source file containing classes, the file name consists of the case-sensitive name of the top-level class (of which there is exactly one), plus the .java extension. + +2.2 File encoding: UTF-8 + +Source files are encoded in UTF-8. + +2.3 Special characters +2.3.1 Whitespace characters + +Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that: + +All other whitespace characters are escaped in char and string literals and in text blocks. +Tab characters are not used for indentation. +2.3.2 Special escape sequences + +For any character that has a special escape sequence (\b, \t, \n, \f, \r, \s, \", \' and \\), that sequence is used rather than the corresponding octal (e.g. \012) or Unicode (e.g. \u000a) escape. + +2.3.3 Non-ASCII characters + +For the remaining non-ASCII characters, either the actual Unicode character (e.g. ∞) or the equivalent Unicode escape (e.g. \u221e) is used. The choice depends only on which makes the code easier to read and understand, although Unicode escapes outside string literals and comments are strongly discouraged. + +Tip: In the Unicode escape case, and occasionally even when actual Unicode characters are used, an explanatory comment can be very helpful. + +Examples: + +Example Discussion +String unitAbbrev = "μs"; Best: perfectly clear even without a comment. +String unitAbbrev = "\u03bcs"; // "μs" Allowed, but there's no reason to do this. +String unitAbbrev = "\u03bcs"; // Greek letter mu, "s" Allowed, but awkward and prone to mistakes. +String unitAbbrev = "\u03bcs"; Poor: the reader has no idea what this is. +return '\ufeff' + content; // byte order mark Good: use escapes for non-printable characters, and comment if necessary. + +Tip: Never make your code less readable simply out of fear that some programs might not handle non-ASCII characters properly. If that should happen, those programs are broken and they must be fixed. + +3 Source file structure + +An ordinary source file consists of these sections, in order: + +License or copyright information, if present +Package declaration +Imports +Exactly one top-level class declaration + +Exactly one blank line separates each section that is present. + +A package-info.java file is the same, but without the class declaration. + +A module-info.java file does not contain a package declaration and replaces the class declaration with a module declaration, but otherwise follows the same structure. + +3.1 License or copyright information, if present + +If license or copyright information belongs in a file, it belongs here. + +3.2 Package declaration + +The package declaration is not line-wrapped. The column limit (Section 4.4, Column limit: 100) does not apply to package declarations. + +3.3 Imports +3.3.1 No wildcard imports + +Wildcard ("on-demand") imports, static or otherwise, are not used. + +3.3.2 No line-wrapping + +Imports are not line-wrapped. The column limit (Section 4.4, Column limit: 100) does not apply to imports. + +3.3.3 Ordering and spacing + +Imports are ordered as follows: + +All static imports in a single group. +All non-static imports in a single group. + +If there are both static and non-static imports, a single blank line separates the two groups. There are no other blank lines between imports. + +Within each group the imported names appear in ASCII sort order. (Note: this is not the same as the import lines being in ASCII sort order, since '.' sorts before ';'.) + +3.3.4 No static import for classes + +Static import is not used for static nested classes. They are imported with normal imports. + +3.4 Class declaration +3.4.1 Exactly one top-level class declaration + +Each top-level class resides in a source file of its own. + +3.4.2 Ordering of class contents + +The order you choose for the members and initializers of your class can have a great effect on learnability. However, there's no single correct recipe for how to do it; different classes may order their contents in different ways. + +What is important is that each class uses some logical order, which its maintainer could explain if asked. For example, new methods are not just habitually added to the end of the class, as that would yield "chronological by date added" ordering, which is not a logical ordering. + +3.4.2.1 Overloads: never split + +Methods of a class that share the same name appear in a single contiguous group with no other members in between. The same applies to multiple constructors. This rule applies even when modifiers such as static or private differ between the methods or constructors. + +3.5 Module declaration +3.5.1 Ordering and spacing of module directives + +Module directives are ordered as follows: + +All requires directives in a single block. +All exports directives in a single block. +All opens directives in a single block. +All uses directives in a single block. +All provides directives in a single block. + +A single blank line separates each block that is present. + +4 Formatting + +Terminology Note: block-like construct refers to the body of a class, method, constructor, or switch. Note that, by Section 4.8.3.1 on array initializers, any array initializer may optionally be treated as if it were a block-like construct. + +4.1 Braces +4.1.1 Use of optional braces + +Braces are used with if, else, for, do and while statements, even when the body is empty or contains only a single statement. + +Other optional braces, such as those in a lambda expression, remain optional. + +4.1.2 Nonempty blocks: K & R style + +Braces follow the Kernighan and Ritchie style for nonempty blocks and block-like constructs: + +No line break before the opening brace, except as detailed below. +Line break after the opening brace. +Line break before the closing brace. +Line break after the closing brace, only if that brace terminates a statement or terminates the body of a method, constructor, or named class. For example, there is no line break after the brace if it is followed by else or a comma. + +Exception: In places where these rules allow a single statement ending with a semicolon (;), a block of statements can appear, and the opening brace of this block is preceded by a line break. Blocks like these are typically introduced to limit the scope of local variables. + +Examples: + +return () -> { + while (condition()) { + method(); + } +}; + +return new MyClass() { + @Override public void method() { + if (condition()) { + try { + something(); + } catch (ProblemException e) { + recover(); + } + } else if (otherCondition()) { + somethingElse(); + } else { + lastThing(); + } + { + int x = foo(); + frob(x); + } + } +}; + +A few exceptions for enum classes are given in Section 4.8.1, Enum classes. + +4.1.3 Empty blocks: may be concise + +An empty block or block-like construct may be in K & R style (as described in Section 4.1.2). Alternatively, it may be closed immediately after it is opened, with no characters or line break in between ({}), unless it is part of a multi-block statement (one that directly contains multiple blocks: if/else or try/catch/finally). + +Examples: + + // This is acceptable + void doNothing() {} + + // This is equally acceptable + void doNothingElse() { + } + // This is not acceptable: No concise empty blocks in a multi-block statement + try { + doSomething(); + } catch (Exception e) {} +4.2 Block indentation: +2 spaces + +Each time a new block or block-like construct is opened, the indent increases by two spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block. (See the example in Section 4.1.2, Nonempty blocks: K & R Style.) + +4.3 One statement per line + +Each statement is followed by a line break. + +4.4 Column limit: 100 + +Java code has a column limit of 100 characters. A "character" means any Unicode code point. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained in Section 4.5, Line-wrapping. + +Each Unicode code point counts as one character, even if its display width is greater or less. For example, if using fullwidth characters, you may choose to wrap the line earlier than where this rule strictly requires. + +Exceptions: + +Lines where obeying the column limit is not possible (for example, a long URL in Javadoc, or a long JSNI method reference). +package declarations and imports (see Sections 3.2 Package declarations and 3.3 Imports). +Contents of text blocks. +Command lines in a comment that may be copied-and-pasted into a shell. +Very long identifiers, on the rare occasions they are called for, are allowed to exceed the column limit. In that case, the valid wrapping for the surrounding code is as produced by google-java-format. +4.5 Line-wrapping + +Terminology Note: When code that might otherwise occupy a single line is divided into multiple lines, this activity is called line-wrapping. + +There is no comprehensive, deterministic formula showing exactly how to line-wrap in every situation. Very often there are several valid ways to line-wrap the same piece of code. + +Note: While the typical reason for line-wrapping is to avoid overflowing the column limit, even code that would in fact fit within the column limit may be line-wrapped at the author's discretion. + +Tip: Extracting a method or local variable may solve the problem without the need to line-wrap. + +4.5.1 Where to break + +The prime directive of line-wrapping is: prefer to break at a higher syntactic level. Also: + +When a line is broken at a non-assignment operator the break comes before the symbol. (Note that this is not the same practice used in Google style for other languages, such as C++ and JavaScript.) +This also applies to the following "operator-like" symbols: +the dot separator (.) +the two colons of a method reference (::) +an ampersand in a type bound () +a pipe in a catch block (catch (FooException | BarException e)). +When a line is broken at an assignment operator the break typically comes after the symbol, but either way is acceptable. +This also applies to the colon in an enhanced for ("foreach") statement. +A method, constructor, or record-class name stays attached to the open parenthesis (() that follows it. +A comma (,) stays attached to the token that precedes it. +A line is never broken adjacent to the arrow in a lambda or a switch rule, except that a break may come immediately after the arrow if the text following it consists of a single unbraced expression. Examples: +MyLambda lambda = + (String label, Long value, Object obj) -> { + ... + }; + +Predicate predicate = str -> + longExpressionInvolving(str); + +switch (x) { + case ColorPoint(Color color, Point(int x, int y)) -> + handleColorPoint(color, x, y); + ... +} + +Note: The primary goal for line wrapping is to have clear code, not necessarily code that fits in the smallest number of lines. + +4.5.2 Indent continuation lines at least +4 spaces + +When line-wrapping, each line after the first (each continuation line) is indented at least +4 from the original line. + +When there are multiple continuation lines, indentation may be varied beyond +4 as desired. In general, two continuation lines use the same indentation level if and only if they begin with syntactically parallel elements. + +Section 4.6.3 on Horizontal alignment addresses the discouraged practice of using a variable number of spaces to align certain tokens with previous lines. + +4.6 Whitespace +4.6.1 Vertical whitespace (blank lines) + +A single blank line always appears: + +Between consecutive members or initializers of a class: fields, constructors, methods, nested classes, static initializers, and instance initializers. +Exception: A blank line between two consecutive fields (having no other code between them) is optional. Such blank lines are used as needed to create logical groupings of fields. +Exception: Blank lines between enum constants are covered in Section 4.8.1. +As required by other sections of this document (such as Section 3, Source file structure, and Section 3.3, Imports). + +A single blank line may also appear anywhere it improves readability, for example between statements to organize the code into logical subsections. A blank line before the first member or initializer, or after the last member or initializer of the class, is neither encouraged nor discouraged. + +Multiple consecutive blank lines are permitted, but never required (or encouraged). + +4.6.2 Horizontal whitespace + +Beyond where required by the language or other style rules, and apart from within literals, comments and Javadoc, a single ASCII space also appears in the following places only. + +Separating any keyword, such as if, for or catch, from an open parenthesis (() that follows it on that line +Separating any keyword, such as else or catch, from a closing curly brace (}) that precedes it on that line +Before any open curly brace ({), with two exceptions: +@SomeAnnotation({a, b}) (no space is used) +String[][] x = {{"foo"}}; (no space is required between {{, by item 10 below) +On both sides of any binary or ternary operator. This also applies to the following "operator-like" symbols: +the ampersand that separates multiple type bounds: +the pipe for a catch block that handles multiple exceptions: catch (FooException | BarException e) +the colon (:) in an enhanced for ("foreach") statement +the arrow in a lambda expression: (String str) -> str.length() +or switch rule: case "FOO" -> bar(); +but not +the two colons (::) of a method reference, which is written like Object::toString +the dot separator (.), which is written like object.toString() +After ,:; or the closing parenthesis ()) of a cast +Between any content and a double slash (//) which begins a comment. Multiple spaces are allowed. +Between a double slash (//) which begins a comment and the comment's text. Multiple spaces are allowed. +Between the type and identifier of a declaration: List list +Optional just inside both braces of an array initializer +new int[] {5, 6} and new int[] { 5, 6 } are both valid +Between a type annotation and [] or .... + +This rule is never interpreted as requiring or forbidding additional space at the start or end of a line; it addresses only interior space. + +4.6.3 Horizontal alignment: never required + +Terminology Note: Horizontal alignment is the practice of adding a variable number of additional spaces in your code with the goal of making certain tokens appear directly below certain other tokens on previous lines. + +This practice is permitted, but is never required by Google Style. It is not even required to maintain horizontal alignment in places where it was already used. + +Here is an example without alignment, then using alignment: + +private int x; // this is fine +private Color color; // this too + +private int x; // permitted, but future edits +private Color color; // may leave it unaligned + +Tip: Alignment can aid readability, but attempting to preserve alignment for its own sake creates future problems. For example, consider a change that touches only one line. If that change disrupts the previous alignment, it's important **not** to introduce additional changes on nearby lines simply to realign them. Introducing formatting changes on otherwise unaffected lines corrupts version history, slows down reviewers, and exacerbates merge conflicts. These practical concerns take priority over alignment. + +4.7 Grouping parentheses: recommended + +Optional grouping parentheses are omitted only when author and reviewer agree that there is no reasonable chance the code will be misinterpreted without them, nor would they have made the code easier to read. It is not reasonable to assume that every reader has the entire Java operator precedence table memorized. + +4.8 Specific constructs +4.8.1 Enum classes + +After the comma that follows an enum constant, a line break is optional. Additional blank lines (usually just one) are also allowed. This is one possibility: + +private enum Answer { + YES { + @Override public String toString() { + return "yes"; + } + }, + + NO, + MAYBE +} + +An enum class with no methods and no documentation on its constants may optionally be formatted as if it were an array initializer (see Section 4.8.3.1 on array initializers). + +private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS } + +Since enum classes are classes, all other rules for formatting classes apply. + +4.8.2 Variable declarations +4.8.2.1 One variable per declaration + +Every variable declaration (field or local) declares only one variable: declarations such as int a, b; are not used. + +Exception: Multiple variable declarations are acceptable in the header of a for loop. + +4.8.2.2 Declared when needed + +Local variables are not habitually declared at the start of their containing block or block-like construct. Instead, local variables are declared close to the point they are first used (within reason), to minimize their scope. Local variable declarations typically have initializers, or are initialized immediately after declaration. + +4.8.3 Arrays +4.8.3.1 Array initializers: can be "block-like" + +Any array initializer may optionally be formatted as if it were a "block-like construct." For example, the following are all valid (not an exhaustive list): + +new int[] { new int[] { + 0, 1, 2, 3 0, +} 1, + 2, +new int[] { 3, + 0, 1, } + 2, 3 +} new int[] + {0, 1, 2, 3} +4.8.3.2 No C-style array declarations + +The square brackets form a part of the type, not the variable: String[] args, not String args[]. + +4.8.4 Switch statements and expressions + +For historical reasons, the Java language has two distinct syntaxes for switch, which we can call old-style and new-style. New-style switches use an arrow (->) after the switch labels, while old-style switches use a colon (:). + +Terminology Note: Inside the braces of a switch block are either one or more switch rules (new-style); or one or more statement groups (old-style). A switch rule consists of a switch label (case ... or default) followed by -> and an expression, block, or throw. A statement group consists of one or more switch labels each followed by a colon, then one or more statements, or, for the last statement group, zero or more statements. (These definitions match the Java Language Specification, §14.11.) + +4.8.4.1 Indentation + +As with any other block, the contents of a switch block are indented +2. Each switch label starts with this +2 indentation. + +In a new-style switch, a switch rule can be written on a single line if it otherwise follows Google style. (It must not exceed the column limit, and if it contains a non-empty block then there must be a line break after {.) The line-wrapping rules of Section 4.5 apply, including the +4 indent for continuation lines. For a switch rule with a non-empty block after the arrow, the same rules apply as for blocks elsewhere: lines between { and } are indented a further +2 relative to the line with the switch label. + +switch (number) { + case 0, 1 -> handleZeroOrOne(); + case 2 -> + handleTwoWithAnExtremelyLongMethodCallThatWouldNotFitOnTheSameLine(); + default -> { + logger.atInfo().log("Surprising number %s", number); + handleSurprisingNumber(number); + } +} + +In an old-style switch, the colon of each switch label is followed by a line break. The statements within a statement group are indented a further +2. + +4.8.4.2 Fall-through: commented + +Within an old-style switch block, each statement group either terminates abruptly (with a break, continue, return or thrown exception), or is marked with a comment to indicate that execution will or might continue into the next statement group. Any comment that communicates the idea of fall-through is sufficient (typically // fall through). This special comment is not required in the last statement group of the switch block. Example: + +switch (input) { + case 1: + case 2: + prepareOneOrTwo(); + // fall through + case 3: + handleOneTwoOrThree(); + break; + default: + handleLargeNumber(input); +} + +Notice that no comment is needed after case 1:, only at the end of the statement group. + +There is no fall-through in new-style switches. + +4.8.4.3 Exhaustiveness and presence of the default label + +The Java language requires switch expressions and many kinds of switch statements to be exhaustive. That effectively means that every possible value that could be switched on will be matched by one of the switch labels. A switch is exhaustive if it has a default label, but also for example if the value being switched on is an enum and every value of the enum is matched by a switch label. Google Style requires every switch to be exhaustive, even those where the language itself does not require it. This may require adding a default label, even if it contains no code. + +4.8.4.4 Switch expressions + +Switch expressions must be new-style switches: + + return switch (list.size()) { + case 0 -> ""; + case 1 -> list.getFirst(); + default -> String.join(", ", list); + }; +4.8.5 Annotations +4.8.5.1 Type-use annotations + +Type-use annotations appear immediately before the annotated type. An annotation is a type-use annotation if it is meta-annotated with @Target(ElementType.TYPE_USE). Example: + +final @Nullable String name; + +public @Nullable Person getPersonByName(String name); +4.8.5.2 Class, package, and module annotations + +Annotations applying to a class, package, or module declaration appear immediately after the documentation block, and each annotation is listed on a line of its own (that is, one annotation per line). These line breaks do not constitute line-wrapping (Section 4.5, Line-wrapping), so the indentation level is not increased. Examples: + +/** This is a class. */ +@Deprecated +@CheckReturnValue +public final class Frozzler { ... } +/** This is a package. */ +@Deprecated +@CheckReturnValue +package com.example.frozzler; +/** This is a module. */ +@Deprecated +@SuppressWarnings("CheckReturnValue") +module com.example.frozzler { ... } +4.8.5.3 Method and constructor annotations + +The rules for annotations on method and constructor declarations are the same as the previous section. Example: + +@Deprecated +@Override +public String getNameIfPresent() { ... } + +Exception: A single parameterless annotation may instead appear together with the first line of the signature, for example: + +@Override public int hashCode() { ... } +4.8.5.4 Field annotations + +Annotations applying to a field also appear immediately after the documentation block, but in this case, multiple annotations (possibly parameterized) may be listed on the same line; for example: + +@Partial @Mock DataLoader loader; +4.8.5.5 Parameter and local variable annotations + +There are no specific rules for formatting annotations on parameters or local variables (except, of course, when the annotation is a type-use annotation). + +4.8.6 Comments + +This section addresses implementation comments. Javadoc is addressed separately in Section 7, Javadoc. + +Any line break may be preceded by arbitrary whitespace followed by an implementation comment. Such a comment renders the line non-blank. + +4.8.6.1 Block comment style + +Block comments are indented at the same level as the surrounding code. They may be in /* ... */ style or // ... style. For multi-line /* ... */ comments, subsequent lines must start with * aligned with the * on the previous line. + +/* + * This is // And so /* Or you can + * okay. // is this. * even do this. */ + */ + +Comments are not enclosed in boxes drawn with asterisks or other characters. + +Tip: When writing multi-line comments, use the /* ... */ style if you want automatic code formatters to re-wrap the lines when necessary (paragraph-style). Most formatters don't re-wrap lines in // ... style comment blocks. + +4.8.6.2 TODO comments + +Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect. + +A TODO comment begins with the word TODO in all caps, a following colon, and a link to a resource that contains the context, ideally a bug reference. A bug reference is preferable because bugs are tracked and have follow-up comments. Follow this piece of context with an explanatory string introduced with a hyphen -. + +The purpose is to have a consistent TODO format that can be searched to find out how to get more details. + +// TODO: crbug.com/12345678 - Remove this after the 2047q4 compatibility window expires. + + +Avoid adding TODOs that refer to an individual or team as the context: + +// TODO: @yourusername - File an issue and use a '*' for repetition. + + +If your TODO is of the form "At a future date do something" make sure that you either include a very specific date ("Fix by November 2005") or a very specific event ("Remove this code when all clients can handle XML responses."). + +4.8.7 Modifiers + +Class and member modifiers, when present, appear in the order recommended by the Java Language Specification: + +public protected private abstract default static final sealed non-sealed + transient volatile synchronized native strictfp + + +Modifiers on requires module directives, when present, appear in the following order: + +transitive static +4.8.8 Numeric Literals + +long-valued integer literals use an uppercase L suffix, never lowercase (to avoid confusion with the digit 1). For example, 3000000000L rather than 3000000000l. + +4.8.9 Text Blocks + +The opening """ of a text block is always on a new line. That line may either follow the same indentation rules as other constructs, or it may have no indentation at all (so it starts at the left margin). The closing """ is on a new line with the same indentation as the opening """, and may be followed on the same line by further code. Each line of text in the text block is indented at least as much as the opening and closing """. (If a line is indented further, then the string literal defined by the text block will have space at the start of that line.) + +The contents of a text block may exceed the column limit. + +5 Naming +5.1 Rules common to all identifiers + +Identifiers use only ASCII letters and digits, and, in a small number of cases noted below, underscores. Thus each valid identifier name is matched by the regular expression \w+ . + +In Google Style, special prefixes or suffixes are not used. For example, these names are not Google Style: name_, mName, s_name and kName. + +5.2 Rules by identifier type +5.2.1 Package and module names + +Package and module names use only lowercase letters and digits (no underscores). Consecutive words are simply concatenated together. For example, com.example.deepspace, not com.example.deepSpace or com.example.deep_space. + +5.2.2 Class names + +Class names are written in UpperCamelCase. + +Class names are typically nouns or noun phrases. For example, Character or ImmutableList. Interface names may also be nouns or noun phrases (for example, List), but may sometimes be adjectives or adjective phrases instead (for example, Readable). + +There are no specific rules or even well-established conventions for naming annotation types. + +A test class has a name that ends with Test, for example, HashIntegrationTest. If it covers a single class, its name is the name of that class plus Test, for example HashImplTest. + +5.2.3 Method names + +Method names are written in lowerCamelCase. + +Method names are typically verbs or verb phrases. For example, sendMessage or stop. + +Underscores may appear in JUnit test method names to separate logical components of the name, with each component written in lowerCamelCase, for example transferMoney_deductsFromSource. There is no One Correct Way to name test methods. + +5.2.4 Constant names + +Constant names use UPPER_SNAKE_CASE: all uppercase letters, with each word separated from the next by a single underscore. But what is a constant, exactly? + +Constants are static final fields whose contents are deeply immutable and whose methods have no detectable side effects. Examples include primitives, strings, immutable value classes, and anything set to null. If any of the instance's observable state can change, it is not a constant. Merely intending to never mutate the object is not enough. Examples: + +// Constants +static final int NUMBER = 5; +static final ImmutableList NAMES = ImmutableList.of("Ed", "Ann"); +static final Map AGES = ImmutableMap.of("Ed", 35, "Ann", 32); +static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable +static final SomeMutableType[] EMPTY_ARRAY = {}; + +// Not constants +static String nonFinal = "non-final"; +final String nonStatic = "non-static"; +static final Set mutableCollection = new HashSet(); +static final ImmutableSet mutableElements = ImmutableSet.of(mutable); +static final ImmutableMap mutableValues = + ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2); +static final Logger logger = Logger.getLogger(MyClass.getName()); +static final String[] nonEmptyArray = {"these", "can", "change"}; + +These names are typically nouns or noun phrases. + +5.2.5 Non-constant field names + +Non-constant field names (static or otherwise) are written in lowerCamelCase. + +These names are typically nouns or noun phrases. For example, computedValues or index. + +5.2.6 Parameter names + +Parameter names are written in lowerCamelCase. + +One-character parameter names in public methods should be avoided. + +5.2.7 Local variable names + +Local variable names are written in lowerCamelCase. + +Even when final and immutable, local variables are not considered to be constants, and should not be styled as constants. + +5.2.8 Type variable names + +Each type variable is named in one of two styles: + +A single capital letter, optionally followed by a single numeral (such as E, T, X, T2) +A name in the form used for classes (see Section 5.2.2, Class names), followed by the capital letter T (examples: RequestT, FooBarT). +5.3 Camel case: defined + +Sometimes there is more than one reasonable way to convert an English phrase into camel case, such as when acronyms or unusual constructs like "IPv6" or "iOS" are present. To improve predictability, Google Style specifies the following (nearly) deterministic scheme. + +Beginning with the prose form of the name: + +Convert the phrase to plain ASCII and remove any apostrophes. For example, "Müller's algorithm" might become "Muellers algorithm". +Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens). +Recommended: if any word already has a conventional camel-case appearance in common usage, split this into its constituent parts (e.g., "AdWords" becomes "ad words"). Note that a word such as "iOS" is not really in camel case per se; it defies any convention, so this recommendation does not apply. +Now lowercase everything (including acronyms), then uppercase only the first character of: +... each word, to yield upper camel case, or +... each word except the first, to yield lower camel case +Finally, join all the words into a single identifier. Note that the casing of the original words is almost entirely disregarded. + +In very rare circumstances (for example, multipart version numbers), you may need to use underscores to separate adjacent numbers, since numbers do not have upper and lower case variants. + +Examples: + +Prose form Correct Incorrect +"XML HTTP request" XmlHttpRequest XMLHTTPRequest +"new customer ID" newCustomerId newCustomerID +"inner stopwatch" innerStopwatch innerStopWatch +"supports IPv6 on iOS?" supportsIpv6OnIos supportsIPv6OnIOS +"YouTube importer" YouTubeImporter +YoutubeImporter* +"Turn on 2SV" turnOn2sv turnOn2Sv +"Guava 33.4.6" guava33_4_6 guava3346 + +*Acceptable, but not recommended. + +Note: Some words are ambiguously hyphenated in the English language: for example "nonempty" and "non-empty" are both correct, so the method names checkNonempty and checkNonEmpty are likewise both correct. + +6 Programming Practices +6.1 @Override: always used + +A method is marked with the @Override annotation whenever it is legal. This includes a class method overriding a superclass method, a class method implementing an interface method, an interface method respecifying a superinterface method, and an explicitly declared accessor method for a record component. + +Exception: @Override may be omitted when the parent method is @Deprecated. + +6.2 Caught exceptions: not ignored + +It is very rarely correct to do nothing in response to a caught exception. (Typical responses are to log it, or if it is considered "impossible", rethrow it as an AssertionError.) + +When it truly is appropriate to take no action whatsoever in a catch block, the reason this is justified is explained in a comment. + +try { + int i = Integer.parseInt(response); + return handleNumericResponse(i); +} catch (NumberFormatException ok) { + // it's not numeric; that's fine, just continue +} +return handleTextResponse(response); +6.3 Static members: qualified using class + +When a reference to a static class member must be qualified, it is qualified with that class's name, not with a reference or expression of that class's type. + +Foo aFoo = ...; +Foo.aStaticMethod(); // good +aFoo.aStaticMethod(); // bad +somethingThatYieldsAFoo().aStaticMethod(); // very bad +6.4 Finalizers: not used + +Do not override Object.finalize. Finalization support is scheduled for removal. + +7 Javadoc +7.1 Formatting +7.1.1 General form + +The basic formatting of Javadoc blocks is as seen in this example: + +/** + * Multiple lines of Javadoc text are written here, + * wrapped normally... + */ +public int method(String p1) { ... } + +... or in this single-line example: + +/** An especially short bit of Javadoc. */ + +The basic form is always acceptable. The single-line form may be substituted when the entirety of the Javadoc block (including comment markers) can fit on a single line. Note that this only applies when there are no block tags such as @param. + +7.1.2 Paragraphs + +One blank line—that is, a line containing only the aligned leading asterisk (*)—appears between paragraphs, and before the group of block tags if present. Each paragraph except the first has

immediately before the first word, with no space after it. HTML tags for other block-level elements, such as

    or , are not preceded with

    . + +7.1.3 Block tags + +Any of the standard "block tags" that are used appear in the order @param, @return, @throws, @deprecated, and these four types never appear with an empty description. When a block tag doesn't fit on a single line, continuation lines are indented four (or more) spaces from the position of the @. + +7.2 The summary fragment + +Each Javadoc block begins with a brief summary fragment. This fragment is very important: it is the only part of the text that appears in certain contexts such as class and method indexes. + +This is a fragment—a noun phrase or verb phrase, not a complete sentence. It does not begin with A {@code Foo} is a..., or This method returns..., nor does it form a complete imperative sentence like Save the record.. However, the fragment is capitalized and punctuated as if it were a complete sentence. + +Tip: A common mistake is to write simple Javadoc in the form /** @return the customer ID */. This is incorrect, and should be changed to /** Returns the customer ID. */ or /** {@return the customer ID} */. + +7.3 Where Javadoc is used + +At the minimum, Javadoc is present for every visible class, member, or record component, with a few exceptions noted below. A top-level class is visible if it is public; a member is visible if it is public or protected and its containing class is visible; and a record component is visible if its containing record is visible. + +Additional Javadoc content may also be present, as explained in Section 7.3.4, Non-required Javadoc. + +7.3.1 Exception: self-explanatory members + +Javadoc is optional for "simple, obvious" members and record components, such as a getFoo() method, if there really and truly is nothing else worthwhile to say but "the foo". + +Important: it is not appropriate to cite this exception to justify omitting relevant information that a typical reader might need to know. For example, for a record component named canonicalName, don't omit its documentation (with the rationale that it would say only @param canonicalName the canonical name) if a typical reader may have no idea what the term "canonical name" means! + +7.3.2 Exception: overrides + +Javadoc is not always present on a method that overrides a supertype method. + +7.3.4 Non-required Javadoc + +Other classes, members, and record components have Javadoc as needed or desired. + +Whenever an implementation comment would be used to define the overall purpose or behavior of a class or member, that comment is written as Javadoc instead (using /**). + +Non-required Javadoc is not strictly required to follow the formatting rules of Sections 7.1.1, 7.1.2, 7.1.3, and 7.2, though it is of course recommended. diff --git a/ai/skills/javascript_expert/SKILL.md b/ai/skills/javascript_expert/SKILL.md new file mode 100644 index 0000000..1164944 --- /dev/null +++ b/ai/skills/javascript_expert/SKILL.md @@ -0,0 +1,16 @@ +--- +name: javascript_expert +description: Google JavaScript Style Guide Expert. Covers modern JS (ES6+) and Closure-style conventions. +--- +# JavaScript Expert + +Expert in Google JavaScript Style Guide. + +## Core Mandate +Strictly follow [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Principles +- Use const and let. +- Arrow functions preferred for callbacks. +- Minimal global state. +- Proper use of modules and exports. diff --git a/ai/skills/javascript_expert/STYLE_GUIDE.md b/ai/skills/javascript_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..9a6ea67 --- /dev/null +++ b/ai/skills/javascript_expert/STYLE_GUIDE.md @@ -0,0 +1,2660 @@ +Google JavaScript Style Guide +Table of Contents +1 Introduction +1.1 Terminology notes +1.2 Guide notes +2 Source file basics +2.1 File name +2.2 File encoding: UTF-8 +2.3 Special characters +3 Source file structure +3.1 License or copyright information, if present +3.2 @fileoverview JSDoc, if present +3.3 goog.module statement +3.4 ES modules +3.5 goog.setTestOnly +3.6 goog.require and goog.requireType statements +3.7 The file’s implementation +4 Formatting +4.1 Braces +4.2 Block indentation: +2 spaces +4.3 Statements +4.4 Column limit: 80 +4.5 Line-wrapping +4.6 Whitespace +4.7 Grouping parentheses: recommended +4.8 Comments +5 Language features +5.1 Local variable declarations +5.2 Array literals +5.3 Object literals +5.4 Classes +5.5 Functions +5.6 String literals +5.7 Number literals +5.8 Control structures +5.9 this +5.10 Equality Checks +5.11 Disallowed features +6 Naming +6.1 Rules common to all identifiers +6.2 Rules by identifier type +6.3 Camel case: defined +7 JSDoc +7.1 General form +7.2 Markdown +7.3 JSDoc tags +7.4 Line wrapping +7.5 Top/file-level comments +7.6 Class comments +7.7 Enum and typedef comments +7.8 Method and function comments +7.9 Property comments +7.10 Type annotations +7.11 Visibility annotations +8 Policies +8.1 Issues unspecified by Google Style: Be Consistent! +8.2 Compiler warnings +8.3 Deprecation +8.4 Code not in Google Style +8.5 Local style rules +8.6 Generated code: mostly exempt +9 Appendices +9.1 JSDoc tag reference +9.2 Commonly misunderstood style rules +9.3 Style-related tools +9.4 Exceptions for legacy platforms + +Please note: This guide is no longer being updated. Google recommends migrating to TypeScript, and following the TypeScript guide. + +1 Introduction + +This document serves as the complete definition of Google’s coding standards for source code in the JavaScript programming language. A JavaScript source file is described as being in Google Style if and only if it adheres to the rules herein. + +Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this document focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn't clearly enforceable (whether by human or tool). + +1.1 Terminology notes + +In this document, unless otherwise clarified: + +The term comment always refers to implementation comments. We do not use the phrase documentation comments, instead using the common term “JSDoc” for both human-readable text and machine-readable annotations within /** … */. + +This Style Guide uses RFC 2119 terminology when using the phrases must, must not, should, should not, and may. The terms prefer and avoid correspond to should and should not, respectively. Imperative and declarative statements are prescriptive and correspond to must. + +Other terminology notes will appear occasionally throughout the document. + +1.2 Guide notes + +Example code in this document is non-normative. That is, while the examples are in Google Style, they may not illustrate the only stylish way to represent the code. Optional formatting choices made in examples must not be enforced as rules. + +2 Source file basics +2.1 File name + +File names must be all lowercase and may include underscores (_) or dashes (-), but no additional punctuation. Follow the convention that your project uses. Filenames’ extension must be .js. + +2.2 File encoding: UTF-8 + +Source files are encoded in UTF-8. + +2.3 Special characters +2.3.1 Whitespace characters + +Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that + +All other whitespace characters in string literals are escaped, and + +Tab characters are not used for indentation. + +2.3.2 Special escape sequences + +For any character that has a special escape sequence (\', \", \\, \b, \f, \n, \r, \t, \v), that sequence is used rather than the corresponding numeric escape (e.g \x0a, \u000a, or \u{a}). Legacy octal escapes are never used. + +2.3.3 Non-ASCII characters + +For the remaining non-ASCII characters, either the actual Unicode character (e.g. ∞) or the equivalent hex or Unicode escape (e.g. \u221e) is used, depending only on which makes the code easier to read and understand. + +Tip: In the Unicode escape case, and occasionally even when actual Unicode characters are used, an explanatory comment can be very helpful. + +/* Best: perfectly clear even without a comment. */ +const units = 'μs'; + +/* Allowed: but unnecessary as μ is a printable character. */ +const units = '\u03bcs'; // 'μs' + +/* Good: use escapes for non-printable characters with a comment for clarity. */ +return '\ufeff' + content; // Prepend a byte order mark. +/* Poor: the reader has no idea what character this is. */ +const units = '\u03bcs'; + +Tip: Never make your code less readable simply out of fear that some programs might not handle non-ASCII characters properly. If that happens, those programs are broken and they must be fixed. + +3 Source file structure + +All new source files should either be a goog.module file (a file containing a goog.module call) or an ECMAScript (ES) module (uses import and export statements). + +Files consist of the following, in order: + +License or copyright information, if present +@fileoverview JSDoc, if present +goog.module statement, if a goog.module file +ES import statements, if an ES module +goog.require and goog.requireType statements +The file’s implementation + +Exactly one blank line separates each section that is present, except the file's implementation, which may be preceded by 1 or 2 blank lines. + +3.1 License or copyright information, if present + +If license or copyright information belongs in a file, it belongs here. + +3.2 @fileoverview JSDoc, if present + +See 7.5 Top/file-level comments for formatting rules. + +3.3 goog.module statement + +All goog.module files must declare exactly one goog.module name on a single line: lines containing a goog.module declaration must not be wrapped, and are therefore an exception to the 80-column limit. + +The entire argument to goog.module is what defines a namespace. It is the package name (an identifier that reflects the fragment of the directory structure where the code lives) plus, optionally, the main class/enum/interface that it defines concatenated to the end in lowerCamelCase. + +Example: + +goog.module('search.urlHistory.urlHistoryService'); +3.3.1 Hierarchy + +Module namespaces may never be named as a direct child of another module's namespace. + +Disallowed: + +goog.module('foo.bar'); // 'foo.bar.qux' would be fine, though +goog.module('foo.bar.baz'); + +The directory hierarchy reflects the namespace hierarchy, so that deeper-nested children are subdirectories of higher-level parent directories. Note that this implies that owners of “parent” namespace groups are necessarily aware of all child namespaces, since they exist in the same directory. + +3.3.2 goog.module.declareLegacyNamespace + +The single goog.module statement may optionally be followed by a call to goog.module.declareLegacyNamespace();. Avoid goog.module.declareLegacyNamespace() when possible. + +Example: + +goog.module('my.test.helpers'); +goog.module.declareLegacyNamespace(); +goog.setTestOnly(); + +goog.module.declareLegacyNamespace exists to ease the transition from traditional object hierarchy-based namespaces but comes with some naming restrictions. As the child module name must be created after the parent namespace, this name must not be a child or parent of any other goog.module (for example, goog.module('parent'); and goog.module('parent.child'); cannot both exist safely, nor can goog.module('parent'); and goog.module('parent.child.grandchild');). + +3.3.3 goog.module Exports + +Classes, enums, functions, constants, and other symbols are exported using the exports object. Exported symbols may be defined directly on the exports object, or else declared locally and exported separately. Symbols are only exported if they are meant to be used outside the module. Non-exported module-local symbols are not declared @private. There is no prescribed ordering for exported and module-local symbols. + +Examples: + +const /** !Array */ exportedArray = [1, 2, 3]; + +const /** !Array */ moduleLocalArray = [4, 5, 6]; + +/** @return {number} */ +function moduleLocalFunction() { + return moduleLocalArray.length; +} + +/** @return {number} */ +function exportedFunction() { + return moduleLocalFunction() * 2; +} + +exports = {exportedArray, exportedFunction}; +/** @const {number} */ +exports.CONSTANT_ONE = 1; + +/** @const {string} */ +exports.CONSTANT_TWO = 'Another constant'; + +Do not annotate the exports object as @const as it is already treated as a constant by the compiler. + +/** @const */ +exports = {exportedFunction}; + +Do not use default exports as they don't translate easily to ES module semantics. + +exports = FancyClass; + +3.4 ES modules + +ES modules are files that use the import and export keywords. + +3.4.1 Imports + +Import statements must not be line wrapped and are therefore an exception to the 80-column limit. + +3.4.1.1 Import paths + +ES module files must use the import statement to import other ES module files. Do not goog.require another ES module. + +import './sideeffects.js'; + +import * as goog from '../closure/goog/goog.js'; +import * as parent from '../parent.js'; + +import {name} from './sibling.js'; + +3.4.1.1.1 File extensions in import paths + +The .js file extension is not optional in import paths and must always be included. + +import '../directory/file'; +import '../directory/file.js'; +3.4.1.2 Importing the same file multiple times + +Do not import the same file multiple times. This can make it hard to determine the aggregate imports of a file. + +// Imports have the same path, but since it doesn't align it can be hard to see. +import {short} from './long/path/to/a/file.js'; +import {aLongNameThatBreaksAlignment} from './long/path/to/a/file.js'; + +3.4.1.3 Naming imports +3.4.1.3.1 Naming module imports + +Module import names (import * as name) are lowerCamelCase names that are derived from the imported file name. + +import * as fileOne from '../file-one.js'; +import * as fileTwo from '../file_two.js'; +import * as fileThree from '../filethree.js'; +import * as libString from './lib/string.js'; +import * as math from './math/math.js'; +import * as vectorMath from './vector/math.js'; + +Some libraries might commonly use a namespace import prefix that violates this naming scheme, but overbearingly common open source use makes the violating style more readable. The only library that currently falls under this exception is threejs, using the THREE prefix. + +3.4.1.3.2 Naming default imports + +Default import names are derived from the imported file name and follow the rules in 6.2 Rules by identifier type. + +import MyClass from '../my-class.js'; +import myFunction from '../my_function.js'; +import SOME_CONSTANT from '../someconstant.js'; + +Note: In general this should not happen as default exports are banned by this style guide, see 3.4.2.1 Named vs default exports. Default imports are only used to import modules that do not conform to this style guide. + +3.4.1.3.3 Naming named imports + +In general symbols imported via the named import (import {name}) should keep the same name. Avoid aliasing imports (import {SomeThing as SomeOtherThing}). Prefer fixing name collisions by using a module import (import *) or renaming the exports themselves. + +import * as bigAnimals from './biganimals.js'; +import * as domesticatedAnimals from './domesticatedanimals.js'; + +new bigAnimals.Cat(); +new domesticatedAnimals.Cat(); + +If renaming a named import is needed then use components of the imported module's file name or path in the resulting alias. + +import {Cat as BigCat} from './biganimals.js'; +import {Cat as DomesticatedCat} from './domesticatedanimals.js'; + +new BigCat(); +new DomesticatedCat(); + +3.4.2 Exports + +Symbols are only exported if they are meant to be used outside the module. Non-exported module-local symbols are not declared @private. There is no prescribed ordering for exported and module-local symbols. + +3.4.2.1 Named vs default exports + +Use named exports in all code. You can apply the export keyword to a declaration, or use the export {name}; syntax. + +Do not use default exports. Importing modules must give a name to these values, which can lead to inconsistencies in naming across modules. + +// Do not use default exports: +export default class Foo { ... } // BAD! +// Use named exports: +export class Foo { ... } +// Alternate style named exports: +class Foo { ... } + +export {Foo}; + +3.4.2.2 Mutability of exports + +Exported variables must not be mutated outside of module initialization. + +There are alternatives if mutation is needed, including exporting a constant reference to an object that has mutable fields or exporting accessor functions for mutable data. + +// Bad: both foo and mutateFoo are exported and mutated. +export let /** number */ foo = 0; + +/** + * Mutates foo. + */ +export function mutateFoo() { + ++foo; +} + +/** + * @param {function(number): number} newMutateFoo + */ +export function setMutateFoo(newMutateFoo) { + // Exported classes and functions can be mutated! + mutateFoo = () => { + foo = newMutateFoo(foo); + }; +} +// Good: Rather than export the mutable variables foo and mutateFoo directly, +// instead make them module scoped and export a getter for foo and a wrapper for +// mutateFooFunc. +let /** number */ foo = 0; +let /** function(number): number */ mutateFooFunc = (foo) => foo + 1; + +/** @return {number} */ +export function getFoo() { + return foo; +} + +export function mutateFoo() { + foo = mutateFooFunc(foo); +} + +/** @param {function(number): number} mutateFoo */ +export function setMutateFoo(mutateFoo) { + mutateFooFunc = mutateFoo; +} + +3.4.2.3 export from + +export from statements must not be line wrapped and are therefore an exception to the 80-column limit. This applies to both export from flavors. + +export {specificName} from './other.js'; +export * from './another.js'; +3.4.3 Circular Dependencies in ES modules + +Do not create cycles between ES modules, even though the ECMAScript specification allows this. Note that it is possible to create cycles with both the import and export statements. + +// a.js +import './b.js'; +// b.js +import './a.js'; + +// `export from` can cause circular dependencies too! +export {x} from './c.js'; +// c.js +import './b.js'; + +export let x; + +3.4.4 Interoperating with Closure + +3.4.4.1 Referencing goog + +To reference the Closure goog namespace, import Closure's goog.js. + +import * as goog from '../closure/goog/goog.js'; + +const {compute} = goog.require('a.name'); + +export const CONSTANT = compute(); + +goog.js exports only a subset of properties from the global goog that can be used in ES modules. + +3.4.4.2 goog.require in ES modules + +goog.require in ES modules works as it does in goog.module files. You can require any Closure namespace symbol (i.e., symbols created by goog.provide or goog.module) and goog.require will return the value. + +import * as goog from '../closure/goog/goog.js'; +import * as anEsModule from './anEsModule.js'; + +const GoogPromise = goog.require('goog.Promise'); +const myNamespace = goog.require('my.namespace'); + +3.4.4.3 Declaring Closure Module IDs in ES modules + +goog.declareModuleId can be used within ES modules to declare a goog.module-like module ID. This means that this module ID can be goog.required, goog.module.getd etc. as if it were a goog.module that did not call goog.module.declareLegacyNamespace. It does not create the module ID as a globally available JavaScript symbol. + +A goog.require (or goog.module.get) for a module ID from goog.declareModuleId will always return the module object (as if it was import *'d). As a result, the argument to goog.declareModuleId should always end with a lowerCamelCaseName. + +Note: It is an error to call goog.module.declareLegacyNamespace in an ES module, it can only be called from goog.module files. There is no direct way to associate a legacy namespace with an ES module. + +goog.declareModuleId should only be used to upgrade Closure files to ES modules in place, where named exports are used. + +import * as goog from '../closure/goog.js'; + +goog.declareModuleId('my.esm'); + +export class Class {}; +3.5 goog.setTestOnly + +In a goog.module file the goog.module statement and, if present, goog.module.declareLegacyNamespace() statement may optionally be followed by a call to goog.setTestOnly(). + +In an ES module the import statements may optionally be followed by a call to goog.setTestOnly(). + +3.6 goog.require and goog.requireType statements + +Imports are done with goog.require and goog.requireType statements. The names imported by a goog.require statement may be used both in code and in type annotations, while those imported by a goog.requireType may be used in type annotations only. + +The goog.require and goog.requireType statements form a contiguous block with no empty lines. This block follows the goog.module declaration separated by a single empty line. The entire argument to goog.require or goog.requireType is a namespace defined by a goog.module in a separate file. goog.require and goog.requireType statements may not appear anywhere else in the file. + +Each goog.require or goog.requireType is assigned to a single constant alias, or else destructured into several constant aliases. These aliases are the only acceptable way to refer to dependencies in type annotations or code. Fully qualified namespaces must not be used anywhere, except as an argument to goog.require or goog.requireType. + +Exception: Types, variables, and functions declared in externs files have to use their fully qualified name in type annotations and code. + +When goog.require is assigned to a single constant alias, it must match the final dot-separated component of the imported module's namespace. + +Exception: In certain cases, additional components of the namespace can be used to form a longer alias. The resulting alias must retain the original identifier's casing such that it still correctly identifies its type. Longer aliases may be used to disambiguate otherwise identical aliases, or if it significantly improves readability. In addition, a longer alias must be used to prevent masking native types such as Element, Event, Error, Map, and Promise (for a more complete list, see Standard Built-in Objects and Web APIs at MDN). + +When renaming destructured aliases, a space must follow the colon as required in 4.6.2 Horizontal whitespace. + +A file should not contain both a goog.require and a goog.requireType statement for the same namespace. If the imported name is used both in code and in type annotations, it should be imported by a single goog.require statement. + +If a module is imported only for its side effects, the call must be a goog.require (not a goog.requireType) and assignment may be omitted. A comment is required to explain why this is needed and suppress a compiler warning. + +The lines are sorted according to the following rules: All requires with a name on the left hand side come first, sorted alphabetically by those names. Then destructuring requires, again sorted by the names on the left hand side. Finally, any require calls that are standalone (generally these are for modules imported just for their side effects). + +Tip: There’s no need to memorize this order and enforce it manually. You can rely on your IDE to report requires that are not sorted correctly. + +If a long alias or module name would cause a line to exceed the 80-column limit, it must not be wrapped: require lines are an exception to the 80-column limit. + +Example: + +// Standard alias style. +const asserts = goog.require('goog.asserts'); +// Namespace-based alias used to disambiguate. +const testingAsserts = goog.require('goog.testing.asserts'); +// Standard destructuring into aliases. +const {MyClass} = goog.require('some.package'); +const {MyType} = goog.requireType('other.package'); +const {clear, clone} = goog.require('goog.array'); +const {Rgb} = goog.require('goog.color'); +// Namespace-based destructuring into aliases used to disambiguate. +const {MyClass: NsMyClass} = goog.require('other.ns'); +const {SomeType: FooSomeType} = goog.requireType('foo.types'); +const {clear: objectClear, clone: objectClone} = goog.require('goog.object'); +// Namespace-based destructuring into aliases used to prevent masking native type. +const {Element: RendererElement} = goog.require('web.renderer'); +// Out of sequence namespace-based aliases used to improve readability. +// Also, require lines longer than 80 columns must not be wrapped. +const {SomeDataStructure: SomeDataStructureModel} = goog.requireType('identical.package.identifiers.models'); +const {SomeDataStructure: SomeDataStructureProto} = goog.require('proto.identical.package.identifiers'); +// goog.require without an alias in order to trigger side effects. +/** @suppress {extraRequire} Initializes MyFramework. */ +goog.require('my.framework.initialization'); + +Discouraged: + +// Some legacy code uses a "default export" style to export a single class, enum, +// record type, etc. Do not use this pattern in new JS. +// When using a "default export", prefer destructuring into aliases. +const MyClass = goog.require('some.package.MyClass'); +const MyType = goog.requireType('some.package.MyType'); +// If necessary to disambiguate, prefer PackageClass over SomeClass as it is +// closer to the format of the module name. +const SomeClass = goog.require('some.package.Class'); + +Disallowed: + +// Extra terms must come from the namespace. +const MyClassForBizzing = goog.require('some.package.MyClass'); +// Alias must include the entire final namespace component. +const MyClass = goog.require('some.package.MyClassForBizzing'); +// Alias must not mask native type (should be `const JspbMap` here). +const Map = goog.require('jspb.Map'); +// Don't break goog.require lines over 80 columns. +const SomeDataStructure = + goog.require('proto.identical.package.identifiers.SomeDataStructure'); +// Alias must be based on the namespace. +const randomName = goog.require('something.else'); +// Missing a space after the colon. +const {Foo:FooProto} = goog.require('some.package.proto.Foo'); +// goog.requireType without an alias. +goog.requireType('some.package.with.a.Type'); + + +/** + * @param {!some.unimported.Dependency} param All external types used in JSDoc + * annotations must be goog.require'd, unless declared in externs. + */ +function someFunction(param) { + // goog.require lines must be at the top level before any other code. + const alias = goog.require('my.long.name.alias'); + // ... +} +3.7 The file’s implementation + +The actual implementation follows after all dependency information is declared (separated by at least one blank line). + +This may consist of any module-local declarations (constants, variables, classes, functions, etc), as well as any exported symbols. + +4 Formatting + +Terminology Note: block-like construct refers to the body of a class, function, method, or brace-delimited block of code. Note that, by 5.2 Array literals and 5.3 Object literals, any array or object literal may optionally be treated as if it were a block-like construct. + +Tip: Use clang-format. The JavaScript community has invested effort to make sure clang-format does the right thing on JavaScript files. clang-format has integration with several popular editors. + +4.1 Braces +4.1.1 Braces are used for all control structures + +Braces are required for all control structures (i.e. if, else, for, do, while, as well as any others), even if the body contains only a single statement. The first statement of a non-empty block must begin on its own line. + +Disallowed: + +if (someVeryLongCondition()) + doSomething(); + +for (let i = 0; i < foo.length; i++) bar(foo[i]); + +Exception: A simple if statement that can fit entirely on a single line with no wrapping (and that doesn’t have an else) may be kept on a single line with no braces when it improves readability. This is the only case in which a control structure may omit braces and newlines. + +if (shortCondition()) foo(); +4.1.2 Nonempty blocks: K&R style + +Braces follow the Kernighan and Ritchie style (Egyptian brackets) for nonempty blocks and block-like constructs: + +No line break before the opening brace. +Line break after the opening brace. +Line break before the closing brace. +Line break after the closing brace if that brace terminates a statement or the body of a function or class statement, or a class method. Specifically, there is no line break after the brace if it is followed by else, catch, while, or a comma, semicolon, or right-parenthesis. + +Example: + +class InnerClass { + constructor() {} + + /** @param {number} foo */ + method(foo) { + if (condition(foo)) { + try { + // Note: this might fail. + something(); + } catch (err) { + recover(); + } + } + } +} +4.1.3 Empty blocks: may be concise + +An empty block or block-like construct may be closed immediately after it is opened, with no characters, space, or line break in between (i.e. {}), unless it is a part of a multi-block statement (one that directly contains multiple blocks: if/else or try/catch/finally). + +Example: + +function doNothing() {} + +Disallowed: + +if (condition) { + // … +} else if (otherCondition) {} else { + // … +} + +try { + // … +} catch (e) {} +4.2 Block indentation: +2 spaces + +Each time a new block or block-like construct is opened, the indent increases by two spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block. (See the example in 4.1.2 Nonempty blocks: K&R style). + +4.2.1 Array literals: optionally block-like + +Any array literal may optionally be formatted as if it were a “block-like construct.” For example, the following are all valid (not an exhaustive list): + +const a = [ + 0, + 1, + 2, +]; + +const b = + [0, 1, 2]; + +const c = [0, 1, 2]; + +someMethod(foo, [ + 0, 1, 2, +], bar); + +Other combinations are allowed, particularly when emphasizing semantic groupings between elements, but should not be used only to reduce the vertical size of larger arrays. + +4.2.2 Object literals: optionally block-like + +Any object literal may optionally be formatted as if it were a “block-like construct.” The same examples apply as 4.2.1 Array literals: optionally block-like. For example, the following are all valid (not an exhaustive list): + +const a = { + a: 0, + b: 1, +}; + +const b = + {a: 0, b: 1}; +const c = {a: 0, b: 1}; + +someMethod(foo, { + a: 0, b: 1, +}, bar); +4.2.3 Class literals + +Class literals (whether declarations or expressions) are indented as blocks. Do not add semicolons after methods, or after the closing brace of a class declaration (statements—such as assignments—that contain class expressions are still terminated with a semicolon). For inheritance, the extends keyword is sufficient unless the superclass is templatized. Subclasses of templatized types must explicitly specify the template type in an @extends JSDoc annotation, even if it is just passing along the same template name. + +Example: + +/** @template T */ +class Foo { + /** @param {T} x */ + constructor(x) { + /** @type {T} */ + this.x = x; + } +} + +/** @extends {Foo} */ +class Bar extends Foo { + constructor() { + super(42); + } +} + +exports.Baz = class extends Bar { + /** @return {number} */ + method() { + return this.x; + } +}; +/** @extends {Bar} */ // <-- unnecessary @extends +exports.Baz = class extends Bar { + /** @return {number} */ + method() { + return this.x; + } +}; +4.2.4 Function expressions + +When declaring an anonymous function in the list of arguments for a function call, the body of the function is indented two spaces more than the preceding indentation depth. + +Example: + +prefix.something.reallyLongFunctionName('whatever', (a1, a2) => { + // Indent the function body +2 relative to indentation depth + // of the 'prefix' statement one line above. + if (a1.equals(a2)) { + someOtherLongFunctionName(a1); + } else { + andNowForSomethingCompletelyDifferent(a2.parrot); + } +}); + +some.reallyLongFunctionCall(arg1, arg2, arg3) + .thatsWrapped() + .then((result) => { + // Indent the function body +2 relative to the indentation depth + // of the '.then()' call. + if (result) { + result.use(); + } + }); +4.2.5 Switch statements + +As with any other block, the contents of a switch block are indented +2. + +After a switch label, a newline appears, and the indentation level is increased +2, exactly as if a block were being opened. An explicit block may be used if required by lexical scoping. The following switch label returns to the previous indentation level, as if a block had been closed. + +A blank line is optional between a break and the following case. + +Example: + +switch (animal) { + case Animal.BANDERSNATCH: + handleBandersnatch(); + break; + + case Animal.JABBERWOCK: + handleJabberwock(); + break; + + default: + throw new Error('Unknown animal'); +} +4.3 Statements +4.3.1 One statement per line + +Each statement is followed by a line-break. + +4.3.2 Semicolons are required + +Every statement must be terminated with a semicolon. Relying on automatic semicolon insertion is forbidden. + +4.4 Column limit: 80 + +JavaScript code has a column limit of 80 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained in 4.5 Line-wrapping. + +Exceptions: + +goog.module, goog.require and goog.requireType statements (see 3.3 goog.module statement and 3.6 goog.require and goog.requireType statements). +ES module import and export from statements (see 3.4.1 Imports and 3.4.2.3 export from). +Lines where obeying the column limit is not possible or would hinder discoverability. Examples include: +A long URL which should be clickable in source. +A shell command intended to be copied-and-pasted. +A long string literal which may need to be copied or searched for wholly (e.g., a long file path). +4.5 Line-wrapping + +Terminology Note: Line wrapping is breaking a chunk of code into multiple lines to obey column limit, where the chunk could otherwise legally fit in a single line. + +There is no comprehensive, deterministic formula showing exactly how to line-wrap in every situation. Very often there are several valid ways to line-wrap the same piece of code. + +Note: While the typical reason for line-wrapping is to avoid overflowing the column limit, even code that would in fact fit within the column limit may be line-wrapped at the author's discretion. + +Tip: Extracting a method or local variable may solve the problem without the need to line-wrap. + +4.5.1 Where to break + +The prime directive of line-wrapping is: prefer to break at a higher syntactic level. + +Preferred: + +currentEstimate = + calc(currentEstimate + x * currentEstimate) / + 2.0; + +Discouraged: + +currentEstimate = calc(currentEstimate + x * + currentEstimate) / 2.0; + +In the preceding example, the syntactic levels from highest to lowest are as follows: assignment, division, function call, parameters, number constant. + +Operators are wrapped as follows: + +When a line is broken at an operator the break comes after the symbol. (Note that this is not the same practice used in Google style for Java.) +This does not apply to the dot (.), which is not actually an operator. +A method or constructor name stays attached to the open parenthesis (() that follows it. +A comma (,) stays attached to the token that precedes it. +A line break is never added between a return and the return value as this would change the meaning of the code. +JSDoc annotations with type names break after {. This is necessary as annotations with optional types (@const, @private, @param, etc) do not scan the next line. + +Note: The primary goal for line wrapping is to have clear code, not necessarily code that fits in the smallest number of lines. + +4.5.2 Indent continuation lines at least +4 spaces + +When line-wrapping, each line after the first (each continuation line) is indented at least +4 from the original line, unless it falls under the rules of block indentation. + +When there are multiple continuation lines, indentation may be varied beyond +4 as appropriate. In general, continuation lines at a deeper syntactic level are indented by larger multiples of 4, and two lines use the same indentation level if and only if they begin with syntactically parallel elements. + +4.6.3 Horizontal alignment: discouraged addresses the discouraged practice of using a variable number of spaces to align certain tokens with previous lines. + +4.6 Whitespace +4.6.1 Vertical whitespace + +A single blank line appears: + +Between consecutive methods in a class or object literal +Exception: A blank line between two consecutive properties definitions in an object literal (with no other code between them) is optional. Such blank lines are used as needed to create logical groupings of fields. +Within method bodies, sparingly to create logical groupings of statements. Blank lines at the start or end of a function body are not allowed. +Optionally before the first or after the last method in a class or object literal (neither encouraged nor discouraged). +As required by other sections of this document (e.g. 3.6 goog.require and goog.requireType statements). + +Multiple consecutive blank lines are permitted, but never required (nor encouraged). + +4.6.2 Horizontal whitespace + +Use of horizontal whitespace depends on location, and falls into three broad categories: leading (at the start of a line), trailing (at the end of a line), and internal. Leading whitespace (i.e., indentation) is addressed elsewhere. Trailing whitespace is forbidden. + +Beyond where required by the language or other style rules, and apart from literals, comments, and JSDoc, a single internal ASCII space also appears in the following places only. + +Separating any reserved word (such as if, for, or catch) except for function and super, from an open parenthesis (() that follows it on that line. +Separating any reserved word (such as else or catch) from a closing curly brace (}) that precedes it on that line. +Before any open curly brace ({), with two exceptions: +Before an object literal that is the first argument of a function or the first element in an array literal (e.g. foo({a: [{c: d}]})). +In a template expansion, as it is forbidden by the language (e.g. valid: `ab${1 + 2}cd`, invalid: `xy$ {3}z`). +On both sides of any binary or ternary operator. +After a comma (,) or semicolon (;). Note that spaces are never allowed before these characters. +After the colon (:) in an object literal. +On both sides of the double slash (//) that begins an end-of-line comment. Here, multiple spaces are allowed, but not required. +After an open-block comment character and on both sides of close characters (e.g. for short-form type declarations, casts, and parameter name comments: this.foo = /** @type {number} */ (bar); or function(/** string */ foo) {; or baz(/* buzz= */ true)). +4.6.3 Horizontal alignment: discouraged + +Terminology Note: Horizontal alignment is the practice of adding a variable number of additional spaces in your code with the goal of making certain tokens appear directly below certain other tokens on previous lines. + +This practice is permitted, but it is generally discouraged by Google Style. It is not even required to maintain horizontal alignment in places where it was already used. + +Here is an example without alignment, followed by one with alignment. Both are allowed, but the latter is discouraged: + +{ + tiny: 42, // this is great + longer: 435, // this too +}; + +{ + tiny: 42, // permitted, but future edits + longer: 435, // may leave it unaligned +}; + +Tip: Alignment can aid readability, but it creates problems for future maintenance. Consider a future change that needs to touch just one line. This change may leave the formerly-pleasing formatting mangled, and that is allowed. More often it prompts the coder (perhaps you) to adjust whitespace on nearby lines as well, possibly triggering a cascading series of reformattings. That one-line change now has a blast radius. This can at worst result in pointless busywork, but at best it still corrupts version history information, slows down reviewers and exacerbates merge conflicts. + +4.6.4 Function arguments + +Prefer to put all function arguments on the same line as the function name. If doing so would exceed the 80-column limit, the arguments must be line-wrapped in a readable way. To save space, you may wrap as close to 80 as possible, or put each argument on its own line to enhance readability. Indentation should be four spaces. Aligning to the parenthesis is allowed, but discouraged. Below are the most common patterns for argument wrapping: + +// Arguments start on a new line, indented four spaces. Preferred when the +// arguments don't fit on the same line with the function name (or the keyword +// "function") but fit entirely on the second line. Works with very long +// function names, survives renaming without reindenting, low on space. +doSomething( + descriptiveArgumentOne, descriptiveArgumentTwo, descriptiveArgumentThree) { + // … +} + +// If the argument list is longer, wrap at 80. Uses less vertical space, +// but violates the rectangle rule and is thus not recommended. +doSomething(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, + tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { + // … +} + +// Four-space, one argument per line. Works with long function names, +// survives renaming, and emphasizes each argument. +doSomething( + veryDescriptiveArgumentNumberOne, + veryDescriptiveArgumentTwo, + tableModelEventHandlerProxy, + artichokeDescriptorAdapterIterator) { + // … +} +4.7 Grouping parentheses: recommended + +Optional grouping parentheses are omitted only when the author and reviewer agree that there is no reasonable chance that the code will be misinterpreted without them, nor would they have made the code easier to read. It is not reasonable to assume that every reader has the entire operator precedence table memorized. + +Do not use unnecessary parentheses around the entire expression following delete, typeof, void, return, throw, case, in, of, or yield. + +Parentheses are required for type casts: /** @type {!Foo} */ (foo). + +4.8 Comments + +This section addresses implementation comments. JSDoc is addressed separately in 7 JSDoc. + +4.8.1 Block comment style + +Block comments are indented at the same level as the surrounding code. They may be in /* … */ or //-style. For multi-line /* … */ comments, subsequent lines must start with * aligned with the * on the previous line, to make comments obvious with no extra context. + +/* + * This is + * okay. + */ + +// And so +// is this. + +/* This is fine, too. */ + +Comments are not enclosed in boxes drawn with asterisks or other characters. + +Do not use JSDoc (/** … */) for implementation comments. + +4.8.2 Parameter Name Comments + +“Parameter name” comments should be used whenever the value and method name do not sufficiently convey the meaning, and refactoring the method to be clearer is infeasible . Their preferred format is before the value with =: + +someFunction(obviousParam, /* shouldRender= */ true, /* name= */ 'hello'); + +For consistency with surrounding code you may put them after the value without =: + +someFunction(obviousParam, true /* shouldRender */, 'hello' /* name */); +5 Language features + +JavaScript includes many dubious (and even dangerous) features. This section delineates which features may or may not be used, and any additional constraints on their use. + +Language features which are not discussed in this style guide may be used with no recommendations of their usage. + +5.1 Local variable declarations +5.1.1 Use const and let + +Declare all local variables with either const or let. Use const by default, unless a variable needs to be reassigned. The var keyword must not be used. + +5.1.2 One variable per declaration + +Every local variable declaration declares only one variable: declarations such as let a = 1, b = 2; are not used. + +5.1.3 Declared when needed, initialized as soon as possible + +Local variables are not habitually declared at the start of their containing block or block-like construct. Instead, local variables are declared close to the point they are first used (within reason), to minimize their scope, and initialized as soon as possible. + +5.1.4 Declare types as needed + +JSDoc type annotations may be added either on the line above the declaration, or else inline before the variable name if no other JSDoc is present. + +Example: + +const /** !Array */ data = []; + +/** + * Some description. + * @type {!Array} + */ +const data = []; + +Mixing inline and JSDoc styles is not allowed: the compiler will only process the first JsDoc and the inline annotations will be lost. + +/** Some description. */ +const /** !Array */ data = []; + +Tip: There are many cases where the compiler can infer a templatized type but not its parameters. This is particularly the case when the initializing literal or constructor call does not include any values of the template parameter type (e.g., empty arrays, objects, Maps, or Sets), or if the variable is modified in a closure. Local variable type annotations are particularly helpful in these cases since otherwise the compiler will infer the template parameter as unknown. + +5.2 Array literals +5.2.1 Use trailing commas + +Include a trailing comma whenever there is a line break between the final element and the closing bracket. + +Example: + +const values = [ + 'first value', + 'second value', +]; +5.2.2 Do not use the variadic Array constructor + +The constructor is error-prone if arguments are added or removed. Use a literal instead. + +Disallowed: + +const a1 = new Array(x1, x2, x3); +const a2 = new Array(x1, x2); +const a3 = new Array(x1); +const a4 = new Array(); + +This works as expected except for the third case: if x1 is a whole number then a3 is an array of size x1 where all elements are undefined. If x1 is any other number, then an exception will be thrown, and if it is anything else then it will be a single-element array. + +Instead, write + +const a1 = [x1, x2, x3]; +const a2 = [x1, x2]; +const a3 = [x1]; +const a4 = []; + +Explicitly allocating an array of a given length using new Array(length) is allowed when appropriate. + +5.2.3 Non-numeric properties + +Do not define or use non-numeric properties on an array (other than length). Use a Map (or Object) instead. + +5.2.4 Destructuring + +Array literals may be used on the left-hand side of an assignment to perform destructuring (such as when unpacking multiple values from a single array or iterable). A final rest element may be included (with no space between the ... and the variable name). Elements should be omitted if they are unused. + +const [a, b, c, ...rest] = generateResults(); +let [, b,, d] = someArray; + +Destructuring may also be used for function parameters (note that a parameter name is required but ignored). Always specify [] as the default value if a destructured array parameter is optional, and provide default values on the left hand side: + +/** @param {!Array=} param1 */ +function optionalDestructuring([a = 4, b = 2] = []) { … }; + +Disallowed: + +function badDestructuring([a, b] = [4, 2]) { … }; + +Tip: For (un)packing multiple values into a function’s parameter or return, prefer object destructuring to array destructuring when possible, as it allows naming the individual elements and specifying a different type for each. + +5.2.5 Spread operator + +Array literals may include the spread operator (...) to flatten elements out of one or more other iterables. The spread operator should be used instead of more awkward constructs with Array.prototype. There is no space after the .... + +Example: + +[...foo] // preferred over Array.prototype.slice.call(foo) +[...foo, ...bar] // preferred over foo.concat(bar) +5.3 Object literals +5.3.1 Use trailing commas + +Include a trailing comma whenever there is a line break between the final property and the closing brace. + +5.3.2 Do not use the Object constructor + +While Object does not have the same problems as Array, it is still disallowed for consistency. Use an object literal ({} or {a: 0, b: 1, c: 2}) instead. + +5.3.3 Do not mix quoted and unquoted keys + +Object literals may represent either structs (with unquoted keys and/or symbols) or dicts (with quoted and/or computed keys). Do not mix these key types in a single object literal. + +Disallowed: + +{ + width: 42, // struct-style unquoted key + 'maxWidth': 43, // dict-style quoted key +} + +This also extends to passing the property name to functions, like hasOwnProperty. In particular, doing so will break in compiled code because the compiler cannot rename/obfuscate the string literal. + +Disallowed: + +/** @type {{width: number, maxWidth: (number|undefined)}} */ +const o = {width: 42}; +if (o.hasOwnProperty('maxWidth')) { + ... +} + +This is best implemented as: + +/** @type {{width: number, maxWidth: (number|undefined)}} */ +const o = {width: 42}; +if (o.maxWidth != null) { + ... +} +5.3.4 Computed property names + +Computed property names (e.g., {['key' + foo()]: 42}) are allowed, and are considered dict-style (quoted) keys (i.e., must not be mixed with non-quoted keys) unless the computed property is a symbol (e.g., [Symbol.iterator]). Enum values may also be used for computed keys, but should not be mixed with non-enum keys in the same literal. + +5.3.5 Method shorthand + +Methods can be defined on object literals using the method shorthand ({method() {… }}) in place of a colon immediately followed by a function or arrow function literal. + +Example: + +return { + stuff: 'candy', + method() { + return this.stuff; // Returns 'candy' + }, +}; + +Note that this in a method shorthand or function refers to the object literal itself whereas this in an arrow function refers to the scope outside the object literal. + +Example: + +class { + getObjectLiteral() { + this.stuff = 'fruit'; + return { + stuff: 'candy', + method: () => this.stuff, // Returns 'fruit' + }; + } +} +5.3.6 Shorthand properties + +Shorthand properties are allowed on object literals. + +Example: + +const foo = 1; +const bar = 2; +const obj = { + foo, + bar, + method() { return this.foo + this.bar; }, +}; +assertEquals(3, obj.method()); +5.3.7 Destructuring + +Object destructuring patterns may be used on the left-hand side of an assignment to perform destructuring and unpack multiple values from a single object. + +Destructured objects may also be used as function parameters, but should be kept as simple as possible: a single level of unquoted shorthand properties. Deeper levels of nesting and computed properties may not be used in parameter destructuring. Specify any default values in the left-hand-side of the destructured parameter ({str = 'some default'} = {}, rather than {str} = {str: 'some default'}), and if a destructured object is itself optional, it must default to {}. The JSDoc for the destructured parameter may be given any name (the name is unused but is required by the compiler). + +Example: + +/** + * @param {string} ordinary + * @param {{num: (number|undefined), str: (string|undefined)}=} param1 + * num: The number of times to do something. + * str: A string to do stuff to. + */ +function destructured(ordinary, {num, str = 'some default'} = {}) {} + +Disallowed: + +/** @param {{x: {num: (number|undefined), str: (string|undefined)}}} param1 */ +function nestedTooDeeply({x: {num, str}}) {}; +/** @param {{num: (number|undefined), str: (string|undefined)}=} param1 */ +function nonShorthandProperty({num: a, str: b} = {}) {}; +/** @param {{a: number, b: number}} param1 */ +function computedKey({a, b, [a + b]: c}) {}; +/** @param {{a: number, b: string}=} param1 */ +function nontrivialDefault({a, b} = {a: 2, b: 4}) {}; + +Destructuring may also be used for goog.require statements, and in this case must not be wrapped: the entire statement occupies one line, regardless of how long it is (see 3.6 goog.require and goog.requireType statements). + +5.3.8 Enums + +Enumerations are defined by adding the @enum annotation to an object literal. Enums must be module-local or assigned directly on exports, not nested under a type or object. + +Additional properties may not be added to an enum after it is defined. Enums must be constant. All enum values must be either a string literal or a number. + +/** + * Supported temperature scales. + * @enum {string} + */ +const TemperatureScale = { + CELSIUS: 'celsius', + FAHRENHEIT: 'fahrenheit', +}; + +/** + * An enum with two values. + * @enum {number} + */ +const Value = { + /** The value used shall have been the first. */ + FIRST_VALUE: 1, + /** The second among two values. */ + SECOND_VALUE: 2, +}; + +For string enums, all values must be statically initialized and not computed using arithmetic operators, template literal substitution, function calls or even a variable reference. + +const ABSOLUTE_ZERO = '-273°F'; + +/** + * Not supported computed values in string enum. + * @enum {string} + */ +const TemperatureInFahrenheit = { + MIN_POSSIBLE: ABSOLUTE_ZERO, + ZERO_FAHRENHEIT: 0 + '°F', + ONE_FAHRENHEIT: `${Values.FIRST_VALUE}°F`, + TWO_FAHRENHEIT: Values.SECOND_VALUE + '°F', + SOME_FAHRENHEIT: getTemperatureInFahrenheit() + '°F', +}; + +Note: Although TypeScript supports a few more patterns for enum values (e.g A: 'a'+'b', etc), the restriction of only allowing string literals and numbers for enum values is to aid migration to TypeScript. For complex values consider using a const object without @enum. + +5.4 Classes +5.4.1 Constructors + +Constructors are optional. Subclass constructors must call super() before setting any fields or otherwise accessing this. Interfaces should declare non-method properties in the constructor. + +5.4.2 Fields + +Define all of a concrete object’s fields (i.e. all properties other than methods) in the constructor. Annotate fields that are never reassigned with @const (these need not be deeply immutable). Annotate non-public fields with the proper visibility annotation (@private, @protected, @package). @private fields' names may optionally end with an underscore. Fields must not be defined within a nested scope in the constructor nor on a concrete class's prototype. + +Example: + +class Foo { + constructor() { + /** @private @const {!Bar} */ + this.bar_ = computeBar(); + + /** @protected @const {!Baz} */ + this.baz = computeBaz(); + } +} + +Tip: Properties should never be added to or removed from an instance after the constructor is finished, since it significantly hinders VMs’ ability to optimize. If necessary, fields that are initialized later should be explicitly set to undefined in the constructor to prevent later shape changes. Adding @struct to an object will check that undeclared properties are not added/accessed. Classes have this added by default. + +5.4.3 Computed properties + +Computed properties may only be used in classes when the property is a symbol. Dict-style properties (that is, quoted or computed non-symbol keys, as defined in 5.3.3 Do not mix quoted and unquoted keys) are not allowed. A [Symbol.iterator] method should be defined for any classes that are logically iterable. Beyond this, Symbol should be used sparingly. + +Tip: be careful of using any other built-in symbols (e.g., Symbol.isConcatSpreadable) as they are not polyfilled by the compiler and will therefore not work in older browsers. + +5.4.4 Static methods + +Where it does not interfere with readability, prefer module-local functions over private static methods. + +Code should not rely on dynamic dispatch of static methods, because it interferes with Closure Compiler optimizations. Static methods should only be called on the base class itself. Static methods should not be called on variables containing a dynamic instance that may be either the constructor or a subclass constructor (and must be defined with @nocollapse if this is done), and must not be called directly on a subclass that doesn’t define the method itself. Do not access this in static methods. + +Disallowed: + +// Context for the examples below; by itself this code is allowed. +class Base { + /** @nocollapse */ static foo() {} +} +class Sub extends Base {} + +// discouraged: don't call static methods dynamically +function callFoo(cls) { cls.foo(); } + +// Disallowed: don't call static methods on subclasses that don't define it themselves +Sub.foo(); + +// Disallowed: don't access this in static methods. +class Clazz { + static foo() { + return this.staticField; + } +} +Class.staticField = 1; +5.4.5 Old-style class declarations + +While ES6 classes are preferred, there are cases where ES6 classes may not be feasible. For example: + +If there exist or will exist subclasses, including frameworks that create subclasses, that cannot be immediately changed to use ES6 class syntax. If such a class were to use ES6 syntax, all downstream subclasses not using ES6 class syntax would need to be modified. + +Frameworks that require a known this value before calling the superclass constructor, since constructors with ES6 super classes do not have access to the instance this value until the call to super returns. + +In all other ways the style guide still applies to this code: let, const, default parameters, rest, and arrow functions should all be used when appropriate. + +goog.defineClass allows for a class-like definition similar to ES6 class syntax: + +let C = goog.defineClass(S, { + /** + * @param {string} value + */ + constructor(value) { + S.call(this, 2); + /** @const */ + this.prop = value; + }, + + /** + * @param {string} param + * @return {number} + */ + method(param) { + return 0; + }, +}); + +Alternatively, while goog.defineClass should be preferred for all new code, more traditional syntax is also allowed. + +/** + * @constructor @extends {S} + * @param {string} value + */ +function C(value) { + S.call(this, 2); + /** @const */ + this.prop = value; +} +goog.inherits(C, S); + +/** + * @param {string} param + * @return {number} + */ +C.prototype.method = function(param) { + return 0; +}; + +Per-instance properties should be defined in the constructor after the call to the super class constructor, if there is a super class. Methods should be defined on the prototype of the constructor. + +Defining constructor prototype hierarchies correctly is harder than it first appears! For that reason, it is best to use goog.inherits from the Closure Library . + +5.4.6 Do not manipulate prototypes directly + +The class keyword allows clearer and more readable class definitions than defining prototype properties. Ordinary implementation code has no business manipulating these objects, though they are still useful for defining classes as defined in 5.4.5 Old-style class declarations. Mixins and modifying the prototypes of builtin objects are explicitly forbidden. + +Exception: Framework code (such as Polymer, or Angular) may need to use prototypes, and should not resort to even-worse workarounds to avoid doing so. + +5.4.7 Getters and Setters + +Do not use JavaScript getter and setter properties. They are potentially surprising and difficult to reason about, and have limited support in the compiler. Provide ordinary methods instead. + +Exception: there are situations where defining a getter or setter is unavoidable (e.g. data binding frameworks such as Angular and Polymer, or for compatibility with external APIs that cannot be adjusted). In these cases only, getters and setters may be used with caution, provided they are defined with the get and set shorthand method keywords or Object.defineProperties (not Object.defineProperty, which interferes with property renaming). Getters must not change observable state. + +Disallowed: + +class Foo { + get next() { return this.nextId++; } +} +5.4.8 Overriding toString + +The toString method may be overridden, but must always succeed and never have visible side effects. + +Tip: Beware, in particular, of calling other methods from toString, since exceptional conditions could lead to infinite loops. + +5.4.9 Interfaces + +Interfaces may be declared with @interface or @record. Interfaces declared with @record can be explicitly (i.e. via @implements) or implicitly implemented by a class or object literal. + +All methods on an interface must be non-static and method bodies must be empty blocks. Fields must be declared as uninitialized members in the class constructor. + +Example: + +/** + * Something that can frobnicate. + * @record + */ +class Frobnicator { + constructor() { + /** @type {number} The number of attempts before giving up. */ + this.attempts; + } + + /** + * Performs the frobnication according to the given strategy. + * @param {!FrobnicationStrategy} strategy + */ + frobnicate(strategy) {} +} + +5.4.10 Abstract Classes + +Use abstract classes when appropriate. Abstract classes and methods must be annotated with @abstract. Do not use goog.abstractMethod. See abstract classes and methods. + +5.4.11 Do not create static container classes + +Do not use container classes with only static methods or properties for the sake of namespacing. + +// container.js +// Bad: Container is an exported class that has only static methods and fields. +class Container { + /** @return {number} */ + static bar() { + return 1; + } +} + +/** @const {number} */ +Container.FOO = 1; + +exports = {Container}; + +Instead, export individual constants and functions: + +/** @return {number} */ +exports.bar = () => { + return 1; +} + +/** @const {number} */ +exports.FOO = 1; +5.4.12 Do not define nested namespaces + +Do not define a nested type (e.g. class, typedef, enum, interface) on another module-local name. + +// foo.js +goog.module('my.namespace'); + +class Foo {...} + +Foo.Bar = class {...}; + +/** @enum {number} */ +Foo.Baz = {...}; + +/** @typedef {{value: number}} */ +Foo.Qux; + +/** @interface */ +Foo.Quuz = class {...} + +exports.Foo = Foo; + +These values should be top-level exports. Choose clear names for these values (e.g. FooConverter for a Converter that could have been nested on Foo). However, when the module name is redundant with part of the class name, consider omitting the redundancy: foo.Foo and foo.Converter rather than foo.Foo and foo.FooConverter. Importers can add the prefix when necessary for clarity (e.g. import {Converter as FooConverter} from './foo';) but cannot easily remove the redundancy when importing the entire module as a namespace. + +// foo.js +goog.module('my.namespace'); + +class Foo {...} + +class FooBar {...} + +/** @enum {string} */ +let FooBaz = {...}; + +/** @typedef {{value: number}} */ +let FooQux; + +/** @interface */ +class FooQuuz {...}; + +export = { + Foo, + FooBar, + FooBaz, + FooQux, + FooQuuz, +}; +5.5 Functions +5.5.1 Top-level functions + +Top-level functions may be defined directly on the exports object, or else declared locally and optionally exported. See 3.3.3 goog.module Exports for more on exports. + +Examples: + +/** @param {string} str */ +exports.processString = (str) => { + // Process the string. +}; +/** @param {string} str */ +const processString = (str) => { + // Process the string. +}; + +exports = {processString}; +5.5.2 Nested functions and closures + +Functions may contain nested function definitions. If it is useful to give the function a name, it should be assigned to a local const. + +5.5.3 Arrow functions + +Arrow functions provide a concise function syntax and simplify scoping this for nested functions. Prefer arrow functions over the function keyword for nested functions (but see 5.3.5 Method shorthand). + +Prefer arrow functions over other this scoping approaches such as f.bind(this), goog.bind(f, this), and const self = this. Arrow functions are particularly useful for calling into callbacks as they permit explicitly specifying which parameters to pass to the callback whereas binding will blindly pass along all parameters. + +The left-hand side of the arrow contains zero or more parameters. Parentheses around the parameters are optional if there is only a single non-destructured parameter. When parentheses are used, inline parameter types may be specified (see 7.8 Method and function comments). + +Tip: Always using parentheses even for single-parameter arrow functions can avoid situations where adding parameters, but forgetting to add parentheses, may result in parseable code which no longer works as intended. + +The right-hand side of the arrow contains the body of the function. By default the body is a block statement (zero or more statements surrounded by curly braces). The body may also be an implicitly returned single expression if either: the program logic requires returning a value, or the void operator precedes a single function or method call (using void ensures undefined is returned, prevents leaking values, and communicates intent). The single expression form is preferred if it improves readability (e.g., for short or simple expressions). + +Examples: + +/** + * Arrow functions can be documented just like normal functions. + * @param {number} numParam A number to add. + * @param {string} strParam Another number to add that happens to be a string. + * @return {number} The sum of the two parameters. + */ +const moduleLocalFunc = (numParam, strParam) => numParam + Number(strParam); + +// Uses the single expression syntax with `void` because the program logic does +// not require returning a value. +getValue((result) => void alert(`Got ${result}`)); + +class CallbackExample { + constructor() { + /** @private {number} */ + this.cachedValue_ = 0; + + // For inline callbacks, you can use inline typing for parameters. + // Uses a block statement because the value of the single expression should + // not be returned and the expression is not a single function call. + getNullableValue((/** ?number */ result) => { + this.cachedValue_ = result == null ? 0 : result; + }); + } +} + +Disallowed: + +/** + * A function with no params and no returned value. + * This single expression body usage is illegal because the program logic does + * not require returning a value and we're missing the `void` operator. + */ +const moduleLocalFunc = () => anotherFunction(); +5.5.4 Generators + +Generators enable a number of useful abstractions and may be used as needed. + +When defining generator functions, attach the * to the function keyword when present, and separate it with a space from the name of the function. When using delegating yields, attach the * to the yield keyword. + +Example: + +/** @return {!Iterator} */ +function* gen1() { + yield 42; +} + +/** @return {!Iterator} */ +const gen2 = function*() { + yield* gen1(); +} + +class SomeClass { + /** @return {!Iterator} */ + * gen() { + yield 42; + } +} +5.5.5 Parameter and return types + +Function parameters and return types should usually be documented with JSDoc annotations. See 7.8 Method and function comments for more information. + +5.5.5.1 Default parameters + +Optional parameters are permitted using the equals operator in the parameter list. Optional parameters must include spaces on both sides of the equals operator, be named exactly like required parameters (i.e., not prefixed with opt_), use the = suffix in their JSDoc type, come after required parameters, and not use initializers that produce observable side effects. All optional parameters for concrete functions must have default values, even if that value is undefined. In contrast to concrete functions, abstract and interface methods must omit default parameter values. + +Example: + +/** + * @param {string} required This parameter is always needed. + * @param {string=} optional This parameter can be omitted. + * @param {!Node=} node Another optional parameter. + */ +function maybeDoSomething(required, optional = '', node = undefined) {} + +/** @interface */ +class MyInterface { + /** + * Interface and abstract methods must omit default parameter values. + * @param {string=} optional + */ + someMethod(optional) {} +} + +Use default parameters sparingly. Prefer destructuring (as in 5.3.7 Destructuring) to create readable APIs when there are more than a small handful of optional parameters that do not have a natural order. + +Note: Unlike Python's default parameters, it is okay to use initializers that return new mutable objects (such as {} or []) because the initializer is evaluated each time the default value is used, so a single object won't be shared across invocations. + +Tip: While arbitrary expressions including function calls may be used as initializers, these should be kept as simple as possible. Avoid initializers that expose shared mutable state, as that can easily introduce unintended coupling between function calls. + +5.5.5.2 Rest parameters + +Use a rest parameter instead of accessing arguments. Rest parameters are typed with a ... prefix in their JSDoc. The rest parameter must be the last parameter in the list. There is no space between the ... and the parameter name. Do not name the rest parameter var_args. Never name a local variable or parameter arguments, which confusingly shadows the built-in name. + +Example: + +/** + * @param {!Array} array This is an ordinary parameter. + * @param {...number} numbers The remainder of arguments are all numbers. + */ +function variadic(array, ...numbers) {} +5.5.6 Generics + +Declare generic functions and methods when necessary with @template TYPE in the JSDoc above the function or method definition. + +5.5.7 Spread operator + +Function calls may use the spread operator (...). Prefer the spread operator to Function.prototype.apply when an array or iterable is unpacked into multiple parameters of a variadic function. There is no space after the .... + +Example: + +function myFunction(...elements) {} +myFunction(...array, ...iterable, ...generator()); +5.6 String literals +5.6.1 Use single quotes + +Ordinary string literals are delimited with single quotes ('), rather than double quotes ("). + +Tip: if a string contains a single quote character, consider using a template string to avoid having to escape the quote. + +Ordinary string literals may not span multiple lines. + +5.6.2 Template literals + +Use template literals (delimited with `) over complex string concatenation, particularly if multiple string literals are involved. Template literals may span multiple lines. + +If a template literal spans multiple lines, it does not need to follow the indentation of the enclosing block, though it may if the added whitespace does not matter. + +Example: + +function arithmetic(a, b) { + return `Here is a table of arithmetic operations: +${a} + ${b} = ${a + b} +${a} - ${b} = ${a - b} +${a} * ${b} = ${a * b} +${a} / ${b} = ${a / b}`; +} +5.6.3 No line continuations + +Do not use line continuations (that is, ending a line inside a string literal with a backslash) in either ordinary or template string literals. Even though ES5 allows this, it can lead to tricky errors if any trailing whitespace comes after the slash, and is less obvious to readers. + +Disallowed: + +const longString = 'This is a very long string that far exceeds the 80 \ + column limit. It unfortunately contains long stretches of spaces due \ + to how the continued lines are indented.'; + +Instead, write + +const longString = 'This is a very long string that far exceeds the 80 ' + + 'column limit. It does not contain long stretches of spaces since ' + + 'the concatenated strings are cleaner.'; +5.7 Number literals + +Numbers may be specified in decimal, hex, octal, or binary. Use exactly 0x, 0o, and 0b prefixes, with lowercase letters, for hex, octal, and binary, respectively. Never include a leading zero unless it is immediately followed by x, o, or b. + +5.8 Control structures +5.8.1 For loops + +With ES6, the language now has three different kinds of for loops. All may be used, though for-of loops should be preferred when possible. + +for-in loops may only be used on dict-style objects (see 5.3.3 Do not mix quoted and unquoted keys), and should not be used to iterate over an array. Object.prototype.hasOwnProperty should be used in for-in loops to exclude unwanted prototype properties. Prefer for-of and Object.keys over for-in when possible. + +5.8.2 Exceptions + +Exceptions are an important part of the language and should be used whenever exceptional cases occur. Always throw Errors or subclasses of Error: never throw string literals or other objects. Always use new when constructing an Error. + +This treatment extends to Promise rejection values as Promise.reject(obj) is equivalent to throw obj; in async functions. + +Custom exceptions provide a great way to convey additional error information from functions. They should be defined and used wherever the native Error type is insufficient. + +Prefer throwing exceptions over ad-hoc error-handling approaches (such as passing an error container reference type, or returning an object with an error property). + +5.8.2.1 Empty catch blocks + +It is very rarely correct to do nothing in response to a caught exception. When it truly is appropriate to take no action whatsoever in a catch block, the reason this is justified is explained in a comment. + +try { + return handleNumericResponse(response); +} catch (ok) { + // it's not numeric; that's fine, just continue +} +return handleTextResponse(response); + +Disallowed: + + try { + shouldFail(); + fail('expected an error'); + } catch (expected) { + } + +Tip: Unlike in some other languages, patterns like the above simply don’t work since this will catch the error thrown by fail. Use assertThrows() instead. + +5.8.3 Switch statements + +Terminology Note: Inside the braces of a switch block are one or more statement groups. Each statement group consists of one or more switch labels (either case FOO: or default:), followed by one or more statements. + +5.8.3.1 Fall-through: commented + +Within a switch block, each statement group either terminates abruptly (with a break, return or thrown exception), or is marked with a comment to indicate that execution will or might continue into the next statement group. Any comment that communicates the idea of fall-through is sufficient (typically // fall through). This special comment is not required in the last statement group of the switch block. + +Example: + +switch (input) { + case 1: + case 2: + prepareOneOrTwo(); + // fall through + case 3: + handleOneTwoOrThree(); + break; + default: + handleLargeNumber(input); +} +5.8.3.2 The default case is present + +Each switch statement includes a default statement group, even if it contains no code. The default statement group must be last. + +5.9 this + +Only use this in class constructors and methods, in arrow functions defined within class constructors and methods, or in functions that have an explicit @this declared in the immediately-enclosing function’s JSDoc. + +Never use this to refer to the global object, the context of an eval, the target of an event, or unnecessarily call()ed or apply()ed functions. + +5.10 Equality Checks + +Use identity operators (===/!==) except in the cases documented below. + +5.10.1 Exceptions Where Coercion is Desirable + +Catching both null and undefined values: + +if (someObjectOrPrimitive == null) { + // Checking for null catches both null and undefined for objects and + // primitives, but does not catch other falsy values like 0 or the empty + // string. +} +5.11 Disallowed features +5.11.1 with + +Do not use the with keyword. It makes your code harder to understand and has been banned in strict mode since ES5. + +5.11.2 Dynamic code evaluation + +Do not use eval or the Function(...string) constructor (except for code loaders). These features are potentially dangerous and simply do not work in CSP environments. + +5.11.3 Automatic semicolon insertion + +Always terminate statements with semicolons (except function and class declarations, as noted above). + +5.11.4 Non-standard features + +Do not use non-standard features. This includes old features that have been removed (e.g., WeakMap.clear), new features that are not yet standardized (e.g., the current TC39 working draft, proposals at any stage, or proposed but not-yet-complete web standards), or proprietary features that are only implemented in some browsers. Use only features defined in the current ECMA-262 or WHATWG standards. (Note that projects writing against specific APIs, such as Chrome extensions or Node.js, can obviously use those APIs). Non-standard language “extensions” (such as those provided by some external transpilers) are forbidden. + +5.11.5 Wrapper objects for primitive types + +Never use new on the primitive object wrappers (Boolean, Number, String, Symbol), nor include them in type annotations. + +Disallowed: + +const /** Boolean */ x = new Boolean(false); +if (x) alert(typeof x); // alerts 'object' - WAT? + +The wrappers may be called as functions for coercing (which is preferred over using + or concatenating the empty string) or creating symbols. + +Example: + +const /** boolean */ x = Boolean(0); +if (!x) alert(typeof x); // alerts 'boolean', as expected +5.11.6 Modifying builtin objects + +Never modify builtin types, either by adding methods to their constructors or to their prototypes. Avoid depending on libraries that do this. Note that the JSCompiler’s runtime library will provide standards-compliant polyfills where possible; nothing else may modify builtin objects. + +Do not add symbols to the global object unless absolutely necessary (e.g. required by a third-party API). + +5.11.7 Omitting () when invoking a constructor + +Never invoke a constructor in a new statement without using parentheses (). + +Disallowed: + +new Foo; + +Use instead: + +new Foo(); + +Omitting parentheses can lead to subtle mistakes. These two lines are not equivalent: + +new Foo().Bar(); +new Foo.Bar(); +6 Naming +6.1 Rules common to all identifiers + +Identifiers use only ASCII letters and digits, and, in a small number of cases noted below, underscores and very rarely (when required by frameworks like Angular) dollar signs. + +Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word. + +errorCount // No abbreviation. +dnsConnectionIndex // Most people know what "DNS" stands for. +referrerUrl // Ditto for "URL". +customerId // "Id" is both ubiquitous and unlikely to be misunderstood. + +Disallowed: + +n // Meaningless. +nErr // Ambiguous abbreviation. +nCompConns // Ambiguous abbreviation. +wgcConnections // Only your group knows what this stands for. +pcReader // Lots of things can be abbreviated "pc". +cstmrId // Deletes internal letters. +kSecondsPerDay // Do not use Hungarian notation. + +Exception: Variables that are in scope for 10 lines or fewer, including arguments that are not part of an exported API, may use short (e.g. single letter) variable names. + +6.2 Rules by identifier type +6.2.1 Package names + +Package names are all lowerCamelCase. For example, my.exampleCode.deepSpace, but not my.examplecode.deepspace or my.example_code.deep_space. + +Exception: The package name may conform to TypeScript's path-based pattern. This is typically all lower case with underscores where present in filenames. + +6.2.2 Class names + +Class, interface, record, and typedef names are written in UpperCamelCase. Unexported classes are simply locals: they are not marked @private. + +Type names are typically nouns or noun phrases. For example, Request, ImmutableView, or VisibilityMode. Additionally, interface names may sometimes be adjectives or adjective phrases instead (for example, Readable). + +6.2.3 Method names + +Method names are written in lowerCamelCase. Names for @private methods may optionally end with a trailing underscore. + +Method names are typically verbs or verb phrases. For example, sendMessage or stop_. Getter and setter methods for properties are never required, but if they are used they should be named getFoo (or optionally isFoo or hasFoo for booleans), or setFoo(value) for setters. + +Underscores may also appear in JsUnit test method names to separate logical components of the name. One typical pattern is test__, for example testPop_emptyStack_throws. There is no One Correct Way to name test methods. + +6.2.4 Enum names + +Enum names are written in UpperCamelCase, similar to classes, and should generally be singular nouns. Individual items within the enum are named in CONSTANT_CASE. + +6.2.5 Constant names + +Constant names use CONSTANT_CASE: all uppercase letters, with words separated by underscores. There is no reason for a constant to be named with a trailing underscore, since private static properties can be replaced by (implicitly private) module locals. + +6.2.5.1 Definition of “constant” + +Every constant is a @const static property or a module-local const declaration, but not all @const static properties and module-local consts are constants. Before choosing constant case, consider whether the field really feels like a deeply immutable constant. For example, if any of that instance's observable state can change, it is almost certainly not a constant. Merely intending to never mutate the object is generally not enough. + +Examples: + +// Constants +const NUMBER = 5; +/** @const */ exports.NAMES = goog.debug.freeze(['Ed', 'Ann']); +/** @enum */ exports.SomeEnum = { ENUM_CONSTANT: 'value' }; + +// Not constants +let letVariable = 'non-const'; + +class MyClass { + constructor() { /** @const {string} */ this.nonStatic = 'non-static'; } +}; +/** @type {string} */ +MyClass.staticButMutable = 'not @const, can be reassigned'; + +const /** Set */ mutableCollection = new Set(); + +const /** MyImmutableContainer */ stillMutable = + new MyImmutableContainer(mutableInner); + +const {Foo} = goog.require('my.foo'); // mirrors imported name + +const logger = log.getLogger('loggers.are.not.immutable'); + +Constants’ names are typically nouns or noun phrases. + +6.2.5.2 Local aliases + +Local aliases should be used whenever they improve readability over fully-qualified names. Follow the same rules as goog.requires (3.6 goog.require and goog.requireType statements), maintaining the last part of the aliased name. Aliases may also be used within functions. Aliases must be const. + +Examples: + +const staticHelper = importedNamespace.staticHelper; +const CONSTANT_NAME = ImportedClass.CONSTANT_NAME; +const {assert, assertInstanceof} = asserts; +6.2.6 Non-constant field names + +Non-constant field names (static or otherwise) are written in lowerCamelCase, with an optional trailing underscore for private fields. + +These names are typically nouns or noun phrases. For example, computedValues or index_. + +6.2.7 Parameter names + +Parameter names are written in lowerCamelCase. Note that this applies even if the parameter expects a constructor. + +One-character parameter names should not be used in public methods. + +Exception: When required by a third-party framework, parameter names may begin with a $. This exception does not apply to any other identifiers (e.g. local variables or properties). + +6.2.8 Local variable names + +Local variable names are written in lowerCamelCase, except for module-local (top-level) constants, as described above. Constants in function scopes are still named in lowerCamelCase. Note that lowerCamelCase is used even if the variable holds a constructor. + +6.2.9 Template parameter names + +Template parameter names should be concise, single-word or single-letter identifiers, and must be all-caps, such as TYPE or THIS. + +6.2.10 Module-local names + +Module-local names that are not exported are implicitly private. They are not marked @private. This applies to classes, functions, variables, constants, enums, and other module-local identifiers. + +6.3 Camel case: defined + +Sometimes there is more than one reasonable way to convert an English phrase into camel case, such as when acronyms or unusual constructs like IPv6 or iOS are present. To improve predictability, Google Style specifies the following (nearly) deterministic scheme. + +Beginning with the prose form of the name: + +Convert the phrase to plain ASCII and remove any apostrophes. For example, Müller's algorithm might become Muellers algorithm. +Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens). +Recommended: if any word already has a conventional camel case appearance in common usage, split this into its constituent parts (e.g., AdWords becomes ad words). Note that a word such as iOS is not really in camel case per se; it defies any convention, so this recommendation does not apply. +Now lowercase everything (including acronyms), then uppercase only the first character of: +… each word, to yield UpperCamelCase, or +… each word except the first, to yield lowerCamelCase +Finally, join all the words into a single identifier. + +Note that the casing of the original words is almost entirely disregarded. + +Examples of lowerCamelCase: + +Prose form Correct Incorrect +XML HTTP request xmlHttpRequest XMLHTTPRequest +new customer ID newCustomerId newCustomerID +inner stopwatch innerStopwatch innerStopWatch +supports IPv6 on iOS? supportsIpv6OnIos supportsIPv6OnIOS +YouTube importer youTubeImporter youtubeImporter* + +*Acceptable, but not recommended. + +For examples of UpperCamelCase, uppercase the first letter of each correct lowerCamelCase example. + +Note: Some words are ambiguously hyphenated in the English language: for example nonempty and non-empty are both correct, so the method names checkNonempty and checkNonEmpty are likewise both correct. + +7 JSDoc + +JSDoc is used on all classes, fields, and methods. + +7.1 General form + +The basic formatting of JSDoc blocks is as seen in this example: + +/** + * Multiple lines of JSDoc text are written here, + * wrapped normally. + * @param {number} arg A number to do something to. + */ +function doSomething(arg) { … } + +or in this single-line example: + +/** @const @private {!Foo} A short bit of JSDoc. */ +this.foo_ = foo; + +If a single-line comment overflows into multiple lines, it must use the multi-line style with /** and */ on their own lines. + +Many tools extract metadata from JSDoc comments to perform code validation and optimization. As such, these comments must be well-formed. + +7.2 Markdown + +JSDoc is written in Markdown, though it may include HTML when necessary. + +Note that tools that automatically extract JSDoc (e.g. JsDossier) will often ignore plain text formatting, so if you did this: + +/** + * Computes weight based on three factors: + * items sent + * items received + * last timestamp + */ + +it would come out like this: + +Computes weight based on three factors: items sent items received last timestamp + + +Instead, write a Markdown list: + +/** + * Computes weight based on three factors: + * + * - items sent + * - items received + * - last timestamp + */ +7.3 JSDoc tags + +Google style allows a subset of JSDoc tags. See 9.1 JSDoc tag reference for the complete list. Most tags must occupy their own line, with the tag at the beginning of the line. + +Disallowed: + +/** + * The "param" tag must occupy its own line and may not be combined. + * @param {number} left @param {number} right + */ +function add(left, right) { ... } + +Simple tags that do not require any additional data (such as @private, @const, @final, @export) may be combined onto the same line, along with an optional type when appropriate. + +/** + * Place more complex annotations (like "implements" and "template") + * on their own lines. Multiple simple tags (like "export" and "final") + * may be combined in one line. + * @export @final + * @implements {Iterable} + * @template TYPE + */ +class MyClass { + /** + * @param {!ObjType} obj Some object. + * @param {number=} num An optional number. + */ + constructor(obj, num = 42) { + /** @private @const {!Array} */ + this.data_ = [obj, num]; + } +} + +There is no hard rule for when to combine tags, or in which order, but be consistent. + +For general information about annotating types in JavaScript see Annotating JavaScript for the Closure Compiler and Types in the Closure Type System. + +7.4 Line wrapping + +Line-wrapped block tags are indented four spaces. Wrapped description text may be lined up with the description on previous lines, but this horizontal alignment is discouraged. + +/** + * Illustrates line wrapping for long param/return descriptions. + * @param {string} foo This is a param with a description too long to fit in + * one line. + * @return {number} This returns something that has a description too long to + * fit in one line. + */ +exports.method = function(foo) { + return 5; +}; + +Do not indent when wrapping a @desc or @fileoverview description. + +7.5 Top/file-level comments + +A file may have a top-level file overview. A copyright notice, author information, and default visibility level are optional. File overviews are generally recommended whenever a file consists of more than a single class definition. The top level comment is designed to orient readers unfamiliar with the code to what is in this file. If present, it may provide a description of the file's contents and any dependencies or compatibility information. Wrapped lines are not indented. + +Example: + +/** + * @fileoverview Description of file, its uses and information + * about its dependencies. + * @package + */ +7.6 Class comments + +Classes, interfaces and records must be documented with a description and any template parameters, implemented interfaces, visibility, or other appropriate tags. The class description should provide the reader with enough information to know how and when to use the class, as well as any additional considerations necessary to correctly use the class. Textual descriptions may be omitted on the constructor. When defining a class @constructor and @extends annotations are not used with the class keyword unless it extends a generic class. When defining an @interface or a @record, the @extends annotation is used when defining a subclass and the extends keyword is never used. + +/** + * A fancier event target that does cool things. + * @implements {Iterable} + */ +class MyFancyTarget extends EventTarget { + /** + * @param {string} arg1 An argument that makes this more interesting. + * @param {!Array} arg2 List of numbers to be processed. + */ + constructor(arg1, arg2) { + // ... + } +}; + +/** + * Records are also helpful. + * @extends {Iterator} + * @record + * @template TYPE + */ +class Listable { + /** @return {TYPE} The next item in line to be returned. */ + next() {} +} +7.7 Enum and typedef comments + +All enums and typedefs must be documented with appropriate JSDoc tags (@typedef or @enum) on the preceding line. Public enums and typedefs must also have a description. Individual enum items may be documented with a JSDoc comment on the preceding line. + +/** + * A useful type union, which is reused often. + * @typedef {!FruitType|!FruitTypeEnum} + */ +let CoolUnionType; + +/** + * Types of fruits. + * @enum {string} + */ +const FruitTypeEnum = { + /** This kind is very sour. */ + SOUR: 'sour', + /** The less-sour kind. */ + SWEET: 'sweet', +}; + +Typedefs are useful for defining short record types, or aliases for unions, complex functions, or generic types. Typedefs should be avoided for record types with many fields, since they do not allow documenting individual fields, nor using templates or recursive references. For large record types, prefer @record. + +7.8 Method and function comments + +In methods and named functions, parameter and return types must be documented, even in the case of same-signature @overrides. The this type should be documented when necessary. Return type may be omitted if the function has no non-empty return statements. + +Method, parameter, and return descriptions (but not types) may be omitted if they are obvious from the rest of the method’s JSDoc or from its signature. + +Method descriptions begin with a verb phrase that describes what the method does. This phrase is not an imperative sentence, but instead is written in the third person, as if there is an implied This method ... before it. + +If a method overrides a superclass method, it must include an @override annotation. For overridden methods, all @param and @return annotations must be specified explicitly even if no type from the superclass method is refined. This is to align with TypeScript. + +/** A class that does something. */ +class SomeClass extends SomeBaseClass { + /** + * Operates on an instance of MyClass and returns something. + * @param {!MyClass} obj An object that for some reason needs detailed + * explanation that spans multiple lines. + * @param {!OtherClass} obviousOtherClass + * @return {boolean} Whether something occurred. + */ + someMethod(obj, obviousOtherClass) { ... } + + /** + * @param {string} param + * @return {string} + * @override + */ + overriddenMethod(param) { ... } +} + +/** + * Demonstrates how top-level functions follow the same rules. This one + * makes an array. + * @param {TYPE} arg + * @return {!Array} + * @template TYPE + */ +function makeArray(arg) { ... } + +If you only need to document the param and return types of a function, you may optionally use inline JSDocs in the function's signature. These inline JSDocs specify the return and param types without tags. + +function /** string */ foo(/** number */ arg) {...} + +If you need descriptions or tags, use a single JSDoc comment above the method. For example, methods which return values need a @return tag. + +class MyClass { + /** + * @param {number} arg + * @return {string} + */ + bar(arg) {...} +} +// Illegal inline JSDocs. + +class MyClass { + /** @return {string} */ foo() {...} +} + +/** No function description allowed inline here. */ function bar() {...} + +function /** Function description is also illegal here. */ baz() {...} + +In anonymous functions annotations are generally optional. If the automatic type inference is insufficient or explicit annotation improves readability, then annotate param and return types like this: + +promise.then( + /** @return {string} */ + (/** !Array */ items) => { + doSomethingWith(items); + return items[0]; + }); + +For function type expressions, see 7.10.4 Function type expressions. + +7.9 Property comments + +Property types must be documented. The description may be omitted for private properties, if name and type provide enough documentation for understanding the code. + +Publicly exported constants are commented the same way as properties. + +/** My class. */ +class MyClass { + /** @param {string=} someString */ + constructor(someString = 'default string') { + /** @private @const {string} */ + this.someString_ = someString; + + /** @private @const {!OtherType} */ + this.someOtherThing_ = functionThatReturnsAThing(); + + /** + * Maximum number of things per pane. + * @type {number} + */ + this.someProperty = 4; + } +} + +/** + * The number of times we'll try before giving up. + * @const {number} + */ +MyClass.RETRY_COUNT = 33; +7.10 Type annotations + +Type annotations are found on @param, @return, @this, and @type tags, and optionally on @const, @export, and any visibility tags. Type annotations attached to JSDoc tags must always be enclosed in braces. + +7.10.1 Nullability + +The type system defines modifiers ! and ? for non-null and nullable, respectively. These modifiers must precede the type. + +Nullability modifiers have different requirements for different types, which fall into two broad categories: + +Type annotations for primitives (string, number, boolean, symbol, undefined, null) and literals ({function(...): ...} and {{foo: string...}}) are always non-nullable by default. Use the ? modifier to make it nullable, but omit the redundant !. +Reference types (generally, anything in UpperCamelCase, including some.namespace.ReferenceType) refer to a class, enum, record, or typedef defined elsewhere. Since these types may or may not be nullable, it is impossible to tell from the name alone whether it is nullable or not. Always use explicit ? and ! modifiers for these types to prevent ambiguity at use sites. + +Bad: + +const /** MyObject */ myObject = null; // Non-primitive types must be annotated. +const /** !number */ someNum = 5; // Primitives are non-nullable by default. +const /** number? */ someNullableNum = null; // ? should precede the type. +const /** !{foo: string, bar: number} */ record = ...; // Already non-nullable. +const /** MyTypeDef */ def = ...; // Not sure if MyTypeDef is nullable. + +// Not sure if object (nullable), enum (non-nullable, unless otherwise +// specified), or typedef (depends on definition). +const /** SomeCamelCaseName */ n = ...; + +Good: + +const /** ?MyObject */ myObject = null; +const /** number */ someNum = 5; +const /** ?number */ someNullableNum = null; +const /** {foo: string, bar: number} */ record = ...; +const /** !MyTypeDef */ def = ...; +const /** ?SomeCamelCaseName */ n = ...; +7.10.2 Type Casts + +In cases where the compiler doesn't accurately infer the type of an expression, and the assertion functions in goog.asserts cannot remedy it, it is possible to tighten the type by adding a type annotation comment and enclosing the expression in parentheses. Note that the parentheses are required. + +/** @type {number} */ (x) +7.10.3 Template Parameter Types + +Always specify template parameters. This way compiler can do a better job and it makes it easier for readers to understand what code does. + +Bad: + +const /** !Object */ users = {}; +const /** !Array */ books = []; +const /** !Promise */ response = ...; + +Good: + +const /** !Object */ users = {}; +const /** !Array */ books = []; +const /** !Promise */ response = ...; + +const /** !Promise */ thisPromiseReturnsNothingButParameterIsStillUseful = ...; +const /** !Object */ mapOfEverything = {}; + +Cases when template parameters should not be used: + +Object is used for type hierarchy and not as map-like structure. +7.10.4 Function type expressions + +Terminology Note: function type expression refers to a type annotation for function types with the keyword function in the annotation (see examples below). + +Where the function definition is given, do not use a function type expression. Specify parameter and return types with @param and @return, or with inline annotations (see 7.8 Method and function comments). This includes anonymous functions and functions defined and assigned to a const (where the function jsdoc appears above the whole assignment expression). + +Function type expressions are needed, for example, inside @typedef, @param or @return. Use it also for variables or properties of function type, if they are not immediately initialized with the function definition. + + /** @private {function(string): string} */ + this.idGenerator_ = googFunctions.identity; + +When using a function type expression, always specify the return type explicitly. Otherwise the default return type is unknown (?), which leads to strange and unexpected behavior, and is rarely what is actually desired. + +Bad - type error, but no warning given: + +/** @param {function()} generateNumber */ +function foo(generateNumber) { + const /** number */ x = generateNumber(); // No compile-time type error here. +} + +foo(() => 'clearly not a number'); + +Good: + +/** + * @param {function(): *} inputFunction1 Can return any type. + * @param {function(): undefined} inputFunction2 Definitely doesn't return + * anything. + * NOTE: the return type of `foo` itself is safely implied to be {undefined}. + */ +function foo(inputFunction1, inputFunction2) {...} +7.10.5 Whitespace + +Within a type annotation, a single space or line break is required after each comma or colon. Additional line breaks may be inserted to improve readability or avoid exceeding the column limit. These breaks should be chosen and indented following the applicable guidelines (e.g. 4.5 Line-wrapping and 4.2 Block indentation: +2 spaces). No other whitespace is allowed in type annotations. + +Good: + +/** @type {function(string): number} */ + +/** @type {{foo: number, bar: number}} */ + +/** @type {number|string} */ + +/** @type {!Object} */ + +/** @type {function(this: Object, number): string} */ + +/** + * @type {function( + * !SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak, + * !OtherVeryLongTypedef): string} + */ + +/** + * @type {!SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak| + * !OtherVeryLongTypedef} + */ + +Bad: + +// Only put a space after the colon +/** @type {function(string) : number} */ + +// Put spaces after colons and commas +/** @type {{foo:number,bar:number}} */ + +// No space in union types +/** @type {number | string} */ +7.11 Visibility annotations + +Visibility annotations (@private, @package, @protected) may be specified in a @fileoverview block, or on any exported symbol or property. Do not specify visibility for local variables, whether within a function or at the top level of a module. @private names may optionally end with an underscore. + +8 Policies +8.1 Issues unspecified by Google Style: Be Consistent! + +For any style question that isn't settled definitively by this specification, prefer to do what the other code in the same file is already doing. If that doesn't resolve the question, consider emulating the other files in the same package. + +8.2 Compiler warnings +8.2.1 Use a standard warning set + +As far as possible projects should use --warning_level=VERBOSE. + +8.2.2 How to handle a warning + +Before doing anything, make sure you understand exactly what the warning is telling you. If you're not positive why a warning is appearing, ask for help . + +Once you understand the warning, attempt the following solutions in order: + +First, fix it or work around it. Make a strong attempt to actually address the warning, or find another way to accomplish the task that avoids the situation entirely. +Otherwise, determine if it's a false alarm. If you are convinced that the warning is invalid and that the code is actually safe and correct, add a comment to convince the reader of this fact and apply the @suppress annotation. +Otherwise, leave a TODO comment. This is a last resort. If you do this, do not suppress the warning. The warning should be visible until it can be taken care of properly. +8.2.3 Suppress a warning at the narrowest reasonable scope + +Warnings are suppressed at the narrowest reasonable scope, usually that of a single local variable or very small method. Often a variable or method is extracted for that reason alone. + +Example + +/** @suppress {uselessCode} Unrecognized 'use asm' declaration */ +function fn() { + 'use asm'; + return 0; +} + +Even a large number of suppressions in a class is still better than blinding the entire class to this type of warning. + +8.3 Deprecation + +Mark deprecated methods, classes or interfaces with @deprecated annotations. A deprecation comment must include simple, clear directions for people to fix their call sites. + +8.4 Code not in Google Style + +You will occasionally encounter files in your codebase that are not in proper Google Style. These may have come from an acquisition, or may have been written before Google Style took a position on some issue, or may be in non-Google Style for any other reason. + +8.4.1 Reformatting existing code + +When updating the style of existing code, follow these guidelines. + +It is not required to change all existing code to meet current style guidelines. Reformatting existing code is a trade-off between code churn and consistency. Style rules evolve over time and these kinds of tweaks to maintain compliance would create unnecessary churn. However, if significant changes are being made to a file it is expected that the file will be in Google Style. +Be careful not to allow opportunistic style fixes to muddle the focus of a CL. If you find yourself making a lot of style changes that aren’t critical to the central focus of a CL, promote those changes to a separate CL. +8.4.2 Newly added code: use Google Style + +Brand new files use Google Style, regardless of the style choices of other files in the same package. + +When adding new code to a file that is not in Google Style, reformatting the existing code first is recommended, subject to the advice in 8.4.1 Reformatting existing code. + +If this reformatting is not done, then new code should be as consistent as possible with existing code in the same file, but must not violate the style guide. + +8.5 Local style rules + +Teams and projects may adopt additional style rules beyond those in this document, but must accept that cleanup changes may not abide by these additional rules, and must not block such cleanup changes due to violating any additional rules. Beware of excessive rules which serve no purpose. The style guide does not seek to define style in every possible scenario and neither should you. + +8.6 Generated code: mostly exempt + +Source code generated by the build process is not required to be in Google Style. However, any generated identifiers that will be referenced from hand-written source code must follow the naming requirements. As a special exception, such identifiers are allowed to contain underscores, which may help to avoid conflicts with hand-written identifiers. + +9 Appendices +9.1 JSDoc tag reference + +JSDoc serves multiple purposes in JavaScript. In addition to being used to generate documentation it is also used to control tooling. The best known are the Closure Compiler type annotations. + +9.1.1 Type annotations and other Closure Compiler annotations + +Documentation for JSDoc used by the Closure Compiler is described in Annotating JavaScript for the Closure Compiler and Types in the Closure Type System. + +9.1.2 Documentation annotations + +In addition to the JSDoc described in Annotating JavaScript for the Closure Compiler the following tags are common and well supported by various documentation generation tools (such as JsDossier) for purely documentation purposes. + +9.1.2.1 @author or @owner - Not recommended. + +Not recommended. + +Syntax: @author username@google.com (First Last) + +/** + * @fileoverview Utilities for handling textareas. + * @author kuth@google.com (Uthur Pendragon) + */ + +Documents the author of a file or the owner of a test, generally only used in the @fileoverview comment. The @owner tag is used by the unit test dashboard to determine who owns the test results. + +9.1.2.2 @bug + +Syntax: @bug bugnumber + +/** @bug 1234567 */ +function testSomething() { + // … +} + +/** + * @bug 1234568 + * @bug 1234569 + */ +function testTwoBugs() { + // … +} + +Indicates what bugs the given test function regression tests. + +Multiple bugs should each have their own @bug line, to make searching for regression tests as easy as possible. + +9.1.2.3 @code - Deprecated. Do not use. + +Deprecated. Do not use. Use Markdown backticks instead. + +Syntax: {@code ...} + +Historically, `BatchItem` was written as {@code BatchItem}. + +/** Processes pending `BatchItem` instances. */ +function processBatchItems() {} + +Indicates that a term in a JSDoc description is code so it may be correctly formatted in generated documentation. + +9.1.2.4 @desc + +Syntax: @desc Message description + +/** @desc Notifying a user that their account has been created. */ +exports.MSG_ACCOUNT_CREATED = goog.getMsg( + 'Your account has been successfully created.'); +9.1.2.5 @link + +Syntax: {@link ...} + +This tag is used to generate cross-reference links within generated documentation. + +/** Processes pending {@link BatchItem} instances. */ +function processBatchItems() {} + +Historical note: @link tags have also been used to create external links in generated documentation. For external links, always use Markdown's link syntax instead: + +/** + * This class implements a useful subset of the + * [native Event interface](https://dom.spec.whatwg.org/#event). + */ +class ApplicationEvent {} +9.1.2.6 @see + +Syntax: @see Link + +/** + * Adds a single item, recklessly. + * @see #addSafely + * @see goog.Collect + * @see goog.RecklessAdder#add + */ + +Reference a lookup to another class function or method. + +9.1.2.7 @supported + +Syntax: @supported Description + +/** + * @fileoverview Event Manager + * Provides an abstracted interface to the browsers' event systems. + * @supported IE10+, Chrome, Safari + */ + +Used in a fileoverview to indicate what browsers are supported by the file. + +You may also see other types of JSDoc annotations in third-party code. These annotations appear in the JSDoc Toolkit Tag Reference but are not considered part of valid Google style. + +9.1.3 Framework specific annotations + +The following annotations are specific to a particular framework. + +9.1.3.1 @ngInject for Angular 1 +9.1.3.2 @polymerBehavior for Polymer + +https://github.com/google/closure-compiler/wiki/Polymer-Pass + +9.1.4 Notes about standard Closure Compiler annotations + +The following tags used to be standard but are now deprecated. + +9.1.4.1 @expose - Deprecated. Do not use. + +Deprecated. Do not use. Use @export and/or @nocollapse instead. + +9.1.4.2 @inheritDoc - Deprecated. Do not use. + +Deprecated. Do not use. Use @override instead. + +9.2 Commonly misunderstood style rules + +Here is a collection of lesser-known or commonly misunderstood facts about Google Style for JavaScript. (The following are true statements; this is not a list of myths.) + +Neither a copyright statement nor @author credit is required in a source file. (Neither is explicitly recommended, either.) +There is no hard and fast rule governing how to order the members of a class (5.4 Classes). +Empty blocks can usually be represented concisely as {}, as detailed in (4.1.3 Empty blocks: may be concise). +The prime directive of line-wrapping is: prefer to break at a higher syntactic level (4.5.1 Where to break). +Non-ASCII characters are allowed in string literals, comments and JSDoc, and in fact are recommended when they make the code easier to read than the equivalent Unicode escape would (2.3.3 Non-ASCII characters). +9.3 Style-related tools + +The following tools exist to support various aspects of Google Style. + +9.3.1 Closure Compiler + +This program performs type checking and other checks, optimizations and other transformations (such as lowering code to ECMAScript 5). + +9.3.2 clang-format + +This program reformats JavaScript source code into Google Style, and also follows a number of non-required but frequently readability-enhancing formatting practices. The output produced by clang-format is compliant with the style guide. + +clang-format is not required. Authors are allowed to change its output, and reviewers are allowed to ask for such changes; disputes are worked out in the usual way. However, subtrees may choose to opt in to such enforcement locally. + +9.3.3 Closure compiler linter + +This program checks for a variety of missteps and anti-patterns. + +9.3.4 Conformance framework + +The JS Conformance Framework is a tool that is part of the Closure Compiler that provides developers a simple means to specify a set of additional checks to be run against their code base above the standard checks. Conformance checks can, for example, forbid access to a certain property, or calls to a certain function, or missing type information (unknowns). + +These rules are commonly used to enforce critical restrictions (such as defining globals, which could break the codebase) and security patterns (such as using eval or assigning to innerHTML), or more loosely to improve code quality. + +For additional information see the official documentation for the JS Conformance Framework. + +9.4 Exceptions for legacy platforms +9.4.1 Overview + +This section describes exceptions and additional rules to be followed when modern ECMAScript syntax is not available to the code authors. Exceptions to the recommended style are required when modern ECMAScript syntax is not possible and are outlined here: + +Use of var declarations is allowed +Use of arguments is allowed +Optional parameters without default values are allowed +9.4.2 Use var +9.4.2.1 var declarations are NOT block-scoped + +var declarations are scoped to the beginning of the nearest enclosing function, script or module, which can cause unexpected behavior, especially with function closures that reference var declarations inside of loops. The following code gives an example: + +for (var i = 0; i < 3; ++i) { + var iteration = i; + setTimeout(function() { console.log(iteration); }, i*1000); +} + +// logs 2, 2, 2 -- NOT 0, 1, 2 +// because `iteration` is function-scoped, not local to the loop. + +9.4.2.2 Declare variables as close as possible to first use + +Even though var declarations are scoped to the beginning of the enclosing function, var declarations should be as close as possible to their first use, for readability purposes. However, do not put a var declaration inside a block if that variable is referenced outside the block. For example: + +function sillyFunction() { + var count = 0; + for (var x in y) { + // "count" could be declared here, but don't do that. + count++; + } + console.log(count + ' items in y'); +} +9.4.2.3 Use @const for constants variables + +For global declarations where the const keyword would be used, if it were available, annotate the var declaration with @const instead (this is optional for local variables). + +9.4.3 Do not use block scoped functions declarations + +Do not do this: + +if (x) { + function foo() {} +} + +While most JavaScript VMs implemented before ECMAScript 6 support function declarations within blocks it was not standardized. Implementations were inconsistent with each other and with the now-standard ECMAScript behavior for block scoped function declaration. The ECMAScript 5 standard and prior only allow for function declarations in the root statement list of a script or function and explicitly ban them in block scopes in strict mode. + +To get consistent behavior, instead use a var initialized with a function expression to define a function within a block: + +if (x) { + var foo = function() {}; +} +9.4.4 Dependency management with goog.provide/goog.require +9.4.4.1 Summary + +WARNING: goog.provide dependency management is deprecated. All new files, even in projects using goog.provide for older files, should use goog.module. The following rules are for pre-existing goog.provide files only. + +Place all goog.provides first, goog.requires second. Separate provides from requires with an empty line. +Sort the entries alphabetically (uppercase first). +Don't wrap goog.provide and goog.require statements. Exceed 80 columns if necessary. +Only provide top-level symbols. + +goog.provide statements should be grouped together and placed first. All goog.require statements should follow. The two lists should be separated with an empty line. + +Similar to import statements in other languages, goog.provide and goog.require statements should be written in a single line, even if they exceed the 80 column line length limit. + +The lines should be sorted alphabetically, with uppercase letters coming first: + +goog.provide('namespace.MyClass'); +goog.provide('namespace.helperFoo'); + +goog.require('an.extremelyLongNamespace.thatSomeoneThought.wouldBeNice.andNowItIsLonger.Than80Columns'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classes'); +goog.require('goog.dominoes'); + + +All members defined on a class should be in the same file. Only top-level classes should be provided in a file that contains multiple members defined on the same class (e.g. enums, inner classes, etc). + +Do this: + +goog.provide('namespace.MyClass'); + +Not this: + +goog.provide('namespace.MyClass'); +goog.provide('namespace.MyClass.CONSTANT'); +goog.provide('namespace.MyClass.Enum'); +goog.provide('namespace.MyClass.InnerClass'); +goog.provide('namespace.MyClass.TypeDef'); +goog.provide('namespace.MyClass.staticMethod'); + +Members on namespaces may also be provided: + +goog.provide('foo.bar'); +goog.provide('foo.bar.CONSTANT'); +goog.provide('foo.bar.method'); +9.4.4.2 Aliasing with goog.scope + +WARNING: goog.scope is deprecated. New files should not use goog.scope even in projects with existing goog.scope usage. + +goog.scope may be used to shorten references to namespaced symbols in code using goog.provide/goog.require dependency management. + +Only one goog.scope invocation may be added per file. Always place it in the global scope. + +The opening goog.scope(function() { invocation must be preceded by exactly one blank line and follow any goog.provide statements, goog.require statements, or top-level comments. The invocation must be closed on the last line in the file. Append // goog.scope to the closing statement of the scope. Separate the comment from the semicolon by two spaces. + +Similar to C++ namespaces, do not indent under goog.scope declarations. Instead, continue from the 0 column. + +Only make aliases for names that will not be re-assigned to another object (e.g., most constructors, enums, and namespaces). Do not do this (see below for how to alias a constructor): + +goog.scope(function() { +var Button = goog.ui.Button; + +Button = function() { ... }; +... + +Names must be the same as the last property of the global that they are aliasing. + +goog.provide('my.module.SomeType'); + +goog.require('goog.dom'); +goog.require('goog.ui.Button'); + +goog.scope(function() { +var Button = goog.ui.Button; +var dom = goog.dom; + +// Alias new types after the constructor declaration. +my.module.SomeType = function() { ... }; +var SomeType = my.module.SomeType; + +// Declare methods on the prototype as usual: +SomeType.prototype.findButton = function() { + // Button as aliased above. + this.button = new Button(dom.getElement('my-button')); +}; +... +}); // goog.scope +9.4.4.3 goog.forwardDeclare + +Prefer to use goog.requireType instead of goog.forwardDeclare to break circular dependencies between files in the same library. Unlike goog.require, a goog.requireType statement is allowed to import a namespace before it is defined. + +goog.forwardDeclare statements must follow the same style rules as goog.require and goog.requireType. The entire block of goog.forwardDeclare, goog.require and goog.requireType statements is sorted alphabetically. + +goog.forwardDeclare is used in legacy code to break circular references spanning across library boundaries. This pattern however is poorly supported by build tools and should not be used. Code should be organized to avoid circular dependencies across libraries (by splitting/merging libraries). + +9.4.4.4 goog.module.get(name) + +If a goog.provide file depends on a goog.module file, the goog.provide file can not normally refer to the module's exports via a global name. Instead, in addition to goog.require()ing the module, the goog.provide file must fetch the module's export object by calling goog.module.get('module.name'). + +Note: Only calling goog.module.get('module.name') does not create a build-time dependency of your code on the module. The goog.require is needed for the build dependency. + +9.4.4.5 goog.module.declareLegacyNamespace() + +WARNING: goog.module.declareLegacyNamespace is only for transitional use. + +goog.module.declareLegacyNamespace is only for use while migrating a JavaScript file and its consumers from goog.provide to goog.module . Update consumers of your goog.module to use goog.module themselves. Remove calls to goog.module.declareLegacyNamespace whenever possible. + +If you can't update consumers of a legacy namespace from goog.provide to goog.module soon, please wrap the contents of your file in a call to goog.scope, use goog.module.get to import the legacy namespace--and then delete the call to goog.module.declareLegacyNamespace in your goog.module. + +Calling goog.module.declareLegacyNamespace() inside a goog.module(name) will declare the module's namespace as a global name just like a goog.provide() call does. This allows a non goog.module namespace to access the module's exports without calling goog.module.get(name). diff --git a/ai/skills/json_expert/SKILL.md b/ai/skills/json_expert/SKILL.md new file mode 100644 index 0000000..dfd46bc --- /dev/null +++ b/ai/skills/json_expert/SKILL.md @@ -0,0 +1,15 @@ +--- +name: json_expert +description: Google JSON Style Guide Expert. For consistent data exchange formats. +--- +# JSON Expert + +Expert in Google JSON Style Guide. + +## Core Mandate +Strictly follow [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Principles +- Clear property naming. +- Consistent structure for lists and maps. +- Avoid redundant data. diff --git a/ai/skills/json_expert/STYLE_GUIDE.md b/ai/skills/json_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..67f3d79 --- /dev/null +++ b/ai/skills/json_expert/STYLE_GUIDE.md @@ -0,0 +1,400 @@ +Google JSON Style Guide + +Revision 0.9 + +Each style point has a summary for which additional information is available by toggling the accompanying arrow button that looks this way: ▶. You may toggle all summaries with the big arrow button: + +▶ Toggle all summaries +Table of Contents +General Guidelines + +Comments Double Quotes Flattened data vs Structured Hierarchy + + +Property Name Guidelines + +Property Name Format Key Names in JSON Maps Reserved Property Names Singular vs Plural Property Names Naming Conflicts + + +Property Value Guidelines + +Property Value Format Empty/Null Property Values Enum Values + + +Property Value Data Types + +Date Property Values Time Duration Property Values Latitude/Longitude Property Values + + +JSON Structure & Reserved Property Names + + + +Top-Level Reserved Property Names + +apiVersion context id method params data error + + +Reserved Property Names in the data object + +data.kind data.fields data.etag data.id data.lang data.updated data.deleted data.items + + +Reserved Property Names for Paging + +data.currentItemCount data.itemsPerPage data.startIndex data.totalItems data.pagingLinkTemplate data.pageIndex data.totalPages + + +Reserved Property Names for Links + +data.self / data.selfLink data.edit / data.editLink data.next / data.nextLink data.previous / data.previousLink + + +Reserved Property Names in the error object + +error.code error.message error.errors error.errors[].domain error.errors[].reason error.errors[].message error.errors[].location error.errors[].locationType error.errors[].extendedHelp error.errors[].sendReport + + +Property Ordering + +Kind Property Items Property Property Ordering Example + + +Examples + +YouTube JSON API Paging Example + + +Appendix + +Appendix A: Reserved JavaScript Words +Important Note +Display Hidden Details in this Guide +▶ +This style guide contains many details that are initially hidden from view. They are marked by the triangle icon, which you see here on your left. Click it now. You should see "Hooray" appear below. +Introduction + +This style guide documents guidelines and recommendations for building JSON APIs at Google. In general, JSON APIs should follow the spec found at JSON.org. This style guide clarifies and standardizes specific cases so that JSON APIs from Google have a standard look and feel. These guidelines are applicable to JSON requests and responses in both RPC-based and REST-based APIs. + +Definitions + +For the purposes of this style guide, we define the following terms: + +property - a name/value pair inside a JSON object. +property name - the name (or key) portion of the property. +property value - the value portion of the property. +{ + // The name/value pair together is a "property". + "propertyName": "propertyValue" +} + + +Javascript's number type encompasses all floating-point numbers, which is a broad designation. In this guide, number will refer to JavaScript's number type, while integer will refer to integers. + +General Guidelines +Comments +▶ +No comments in JSON objects. +Double Quotes +▶ +Use double quotes. +Flattened data vs Structured Hierarchy +▶ +Data should not be arbitrarily grouped for convenience. +Property Name Guidelines +Property Name Format +▶ +Choose meaningful property names. +Key Names in JSON Maps +▶ +JSON maps can use any Unicode character in key names. +Reserved Property Names +▶ +Certain property names are reserved for consistent use across services. +Singular vs Plural Property Names +▶ +Array types should have plural property names. All other property names should be singular. +Naming Conflicts +▶ +Avoid naming conflicts by choosing a new property name or versioning the API. +Property Value Guidelines +Property Value Format +▶ +Property values must be booleans, numbers, Unicode strings, objects, arrays, or null. +Empty/Null Property Values +▶ +Consider removing empty or null values. +Enum Values +▶ +Enum values should be represented as strings. +Property Value Data Types + +As mentioned above, property value types must be booleans, numbers, strings, objects, arrays, or null. However, it is useful define a set of standard data types when dealing with certain values. These data types will always be strings, but they will be formatted in a specific manner so that they can be easily parsed. + +Date Property Values +▶ +Dates should be formatted as recommended by RFC 3339. +Time Duration Property Values +▶ +Time durations should be formatted as recommended by ISO 8601. +Latitude/Longitude Property Values +▶ +Latitudes/Longitudes should be formatted as recommended by ISO 6709. +JSON Structure & Reserved Property Names + +In order to maintain a consistent interface across APIs, JSON objects should follow the structure outlined below. This structure applies to both requests and responses made with JSON. Within this structure, there are certain property names that are reserved for specific uses. These properties are NOT required; in other words, each reserved property may appear zero or one times. But if a service needs these properties, this naming convention is recommended. Here is a schema of the JSON structure, represented in Orderly format (which in turn can be compiled into a JSONSchema). You can few examples of the JSON structure at the end of this guide. + +object { + string apiVersion?; + string context?; + string id?; + string method?; + object { + string id? + }* params?; + object { + string kind?; + string fields?; + string etag?; + string id?; + string lang?; + string updated?; # date formatted RFC 3339 + boolean deleted?; + integer currentItemCount?; + integer itemsPerPage?; + integer startIndex?; + integer totalItems?; + integer pageIndex?; + integer totalPages?; + string pageLinkTemplate /^https?:/ ?; + object {}* next?; + string nextLink?; + object {}* previous?; + string previousLink?; + object {}* self?; + string selfLink?; + object {}* edit?; + string editLink?; + array [ + object {}*; + ] items?; + }* data?; + object { + integer code?; + string message?; + array [ + object { + string domain?; + string reason?; + string message?; + string location?; + string locationType?; + string extendedHelp?; + string sendReport?; + }*; + ] errors?; + }* error?; +}*; + + +The JSON object has a few top-level properties, followed by either a data object or an error object, but not both. An explanation of each of these properties can be found below. + +Top-Level Reserved Property Names + +The top-level of the JSON object may contain the following properties. + +apiVersion +▶ +Property Value Type: string +Parent: - +context +▶ +Property Value Type: string +Parent: - +id +▶ +Property Value Type: string +Parent: - +method +▶ +Property Value Type: string +Parent: - +params +▶ +Property Value Type: object +Parent: - +data +▶ +Property Value Type: object +Parent: - +error +▶ +Property Value Type: object +Parent: - +Reserved Property Names in the data object + +The data property of the JSON object may contain the following properties. + +data.kind +▶ +Property Value Type: string +Parent: data +data.fields +▶ +Property Value Type: string +Parent: data +data.etag +▶ +Property Value Type: string +Parent: data +data.id +▶ +Property Value Type: string +Parent: data +data.lang +▶ +Property Value Type: string (formatted as specified in BCP 47) +Parent: data (or any child element) +data.updated +▶ +Property Value Type: string (formatted as specified in RFC 3339) +Parent: data +data.deleted +▶ +Property Value Type: boolean +Parent: data (or any child element) +data.items +▶ +Property Value Type: array +Parent: data +Reserved Property Names for Paging + +The following properties are located in the data object, and help page through a list of items. Some of the language and concepts are borrowed from the OpenSearch specification. + +The paging properties below allow for various styles of paging, including: + +Previous/Next paging - Allows user's to move forward and backward through a list, one page at a time. The nextLink and previousLink properties (described in the "Reserved Property Names for Links" section below) are used for this style of paging. +Index-based paging - Allows user's to jump directly to a specific item position within a list of items. For example, to load 10 items starting at item 200, the developer may point the user to a url with the query string ?startIndex=200. +Page-based paging - Allows user's to jump directly to a specific page within the items. This is similar to index-based paging, but saves the developer the extra step of having to calculate the item index for a new page of items. For example, rather than jump to item number 200, the developer could jump to page 20. The urls during page-based paging could use the query string ?page=1 or ?page=20. The pageIndex and totalPages properties are used for this style of paging. + +An example of how to use these properties to implement paging can be found at the end of this guide. + +data.currentItemCount +▶ +Property Value Type: integer +Parent: data +data.itemsPerPage +▶ +Property Value Type: integer +Parent: data +data.startIndex +▶ +Property Value Type: integer +Parent: data +data.totalItems +▶ +Property Value Type: integer +Parent: data +data.pagingLinkTemplate +▶ +Property Value Type: string +Parent: data +data.pageIndex +▶ +Property Value Type: integer +Parent: data +data.totalPages +▶ +Property Value Type: integer +Parent: data +Reserved Property Names for Links + +The following properties are located in the data object, and represent references to other resources. There are two forms of link properties: 1) objects, which can contain any sort of reference (such as a JSON-RPC object), and 2) URI strings, which represent URIs to resources (and will always be suffixed with "Link"). + +data.self / data.selfLink +▶ +Property Value Type: object / string +Parent: data +data.edit / data.editLink +▶ +Property Value Type: object / string +Parent: data +data.next / data.nextLink +▶ +Property Value Type: object / string +Parent: data +data.previous / data.previousLink +▶ +Property Value Type: object / string +Parent: data +Reserved Property Names in the error object + +The error property of the JSON object may contain the following properties. + +error.code +▶ +Property Value Type: integer +Parent: error +error.message +▶ +Property Value Type: string +Parent: error +error.errors +▶ +Property Value Type: array +Parent: error +error.errors[].domain +▶ +Property Value Type: string +Parent: error.errors +error.errors[].reason +▶ +Property Value Type: string +Parent: error.errors +error.errors[].message +▶ +Property Value Type: string +Parent: error.errors +error.errors[].location +▶ +Property Value Type: string +Parent: error.errors +error.errors[].locationType +▶ +Property Value Type: string +Parent: error.errors +error.errors[].extendedHelp +▶ +Property Value Type: string +Parent: error.errors +error.errors[].sendReport +▶ +Property Value Type: string +Parent: error.errors +Property Ordering + +Properties can be in any order within the JSON object. However, in some cases the ordering of properties can help parsers quickly interpret data and lead to better performance. One example is a pull parser in a mobile environment, where performance and memory are critical, and unnecessary parsing should be avoided. + +Kind Property +▶ +kind should be the first property +Items Property +▶ +items should be the last property in the data object +Property Ordering Example +▶ +Examples +YouTube JSON API +▶ +Here's an example of the YouTube JSON API's response object. You can learn more about YouTube's JSON API here: https://code.google.com/apis/youtube/2.0/developers_guide_jsonc.html. +Paging Example +▶ +This example demonstrates how the Google search items could be represented as a JSON object, with special attention to the paging variables. +Appendix +Appendix A: Reserved JavaScript Words +▶ +A list of reserved JavaScript words that should be avoided in property names. + +Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License. + +Revision 0.9 diff --git a/ai/skills/lisp_expert/SKILL.md b/ai/skills/lisp_expert/SKILL.md new file mode 100644 index 0000000..1858ab4 --- /dev/null +++ b/ai/skills/lisp_expert/SKILL.md @@ -0,0 +1,15 @@ +--- +name: lisp_expert +description: Google Common Lisp Style Guide Expert. Use this to ensure all Lisp code and plans follow Google's official style guide. +--- +# Lisp Expert + +Expert in Google Common Lisp Style Guide. + +## Core Mandate +Strictly adhere to the rules and best practices defined in the local [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Guidelines +- Focus on clarity and consistency in S-expressions. +- Use macros judiciously as per the guide. +- Follow naming conventions and formatting rules precisely. diff --git a/ai/skills/lisp_expert/STYLE_GUIDE.md b/ai/skills/lisp_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..5fbb984 --- /dev/null +++ b/ai/skills/lisp_expert/STYLE_GUIDE.md @@ -0,0 +1,314 @@ +Google Common Lisp Style Guide + +Revision 1.28 + +Robert Brown +François-René Rideau +In memoriam Dan Weinreb + +Patterns mean "I have run out of language." — Rich Hickey + +Each style point has a summary for which additional information is available by toggling the accompanying arrow button that looks this way: ▶. You may toggle all summaries with the big arrow button: + +▶ Toggle all summaries +Table of Contents +Meta-Guide + +Must, Should, May, or Not Permission and Forgiveness Conventions Old Code Future Topics + + +General Guidelines + +Principles Priorities Architecture Using Libraries Open-Sourcing Code Development Process + + +Formatting + +Spelling and Abbreviations Line length Indentation File Header Vertical white space Horizontal white space + + +Documentation + +Document everything Comment semicolons Grammar and punctuation Attention Required Domain-Specific Languages + + +Naming + +Symbol guidelines Denote intent, not content Global variables and constants Predicate names Omit library prefixes Packages + + +Language usage guidelines + +Mostly Functional Style Recursion Special variables Assignment Assertions and Conditions Type Checking CLOS + + +Meta-language guidelines + +Macros EVAL-WHEN Read-time evaluation EVAL INTERN and UNINTERN + + +Data Representation + +NIL: empty-list, false and I Don't Know Do not abuse lists Lists vs. structures vs. multiple values Lists vs. Pairs Lists vs. Arrays Lists vs. Sets + + +Proper Forms + +Defining Constants Defining Functions Conditional Expressions Identity, Equality and Comparisons Iteration I/O + + +Optimization + +Avoid Allocation Unsafe Operations DYNAMIC-EXTENT REDUCE vs APPLY Avoid NCONC + + +Pitfalls + +#'FUN vs. 'FUN Pathnames SATISFIES +Important Note +Note: Displaying Hidden Details in this Guide +▶ +This style guide contains many details that are initially hidden from view. They are marked by the triangle icon, which you see here on your left. Click it now. You should see "Hooray" appear below. +Background + +Common Lisp is a powerful multiparadigm programming language. With great power comes great responsibility. + +This guide recommends formatting and stylistic choices designed to make your code easier for other people to understand. For those internal applications and free software libraries that we develop at Google, you should keep within these guidelines when making changes. Note however that each project has its own rules and customs that complement or override these general guidelines; the speed-oriented QPX low fare search engine notably has a very different style and feel from the QRes reservation system. + +If you're writing Common Lisp code outside Google, we invite you to consider these guidelines. You may find some of them useful where they don't conflict with other priorities you have. We welcome remarks and constructive feedback on how to improve our guide, and on what alternate styles work for you and why. + +This guide is not a Common Lisp tutorial. For basic information about the language, please consult Practical Common Lisp. For a language reference, please consult the Common Lisp HyperSpec. For more detailed style guidance, take (with a pinch of salt) a look at Peter Norvig and Kent Pitman's style guide. + +Meta-Guide +Must, Should, May, or Not +▶ +Each guideline's level of importance is indicated by use of the following keywords and phrases, adapted from RFC 2119. +Permission and Forgiveness +▶ +There are cases where transgression of some of these rules is useful or even necessary. In some cases, you must seek permission or obtain forgiveness from the proper people. +Conventions +▶ +You MUST follow conventions. They are not optional. +Old Code +▶ +Fix old code as you go. +Future Topics +▶ +There are many topics for additional standardization not covered by current version of this document, but deferred to future versions. +General Guidelines +Principles +▶ +There are some basic principles for team software development that every developer must keep in mind. Whenever the detailed guidelines are inadequate, confusing or contradictory, refer back to these principles for guidance: +Every developer's code must be easy for another developer to read, understand, and modify — even if the first developer isn't around to explain it. (This is the "hit by a truck" principle.) +Everybody's code should look the same. Ideally, there should be no way to look at lines of code and recognize it as "Fred's code" by its style. +Be precise. +Be concise. +KISS — Keep It Simple, Stupid. +Use the smallest hammer for the job. +Use common sense. +Keep related code together. Minimize the amount of jumping around someone has to do to understand an area of code. +Priorities +▶ + +When making decisions about how to write a given piece of code, aim for the following -ilities in this priority order: + +Usability by the customer +Debuggability/Testability +Readability/Comprehensibility +Extensibility/Modifiability +Efficiency (of the Lisp code at runtime) +Architecture +▶ +To build code that is robust and maintainable, it matters a lot how the code is divided into components, how these components communicate, how changes propagate as they evolve, and more importantly how the programmers who develop these components communicate as these components evolve. +Using Libraries +▶ +Often, the smallest hammer is to use an existing library. Or one that doesn't exist yet. In such cases, you are encouraged to use or develop such a library, but you must take appropriate precautions. +Open-Sourcing Code +▶ + +If you write a general-purpose library, or modify an existing open-source library, you are encouraged to publish the result separate from your main project and then have your project import it like any other open-source library. + +Development Process +▶ +Development process is outside the scope of this document. However, developers should remember at least these bits: get reviewed, write tests, eliminate warnings, run tests, avoid mass-changes. +Formatting +Spelling and Abbreviations +▶ + +You must use correct spelling in your comments, and most importantly in your identifiers. + +When several correct spellings exist (including American vs English), and there isn't a consensus amongst developers as which to use, you should choose the shorter spelling. + +You must use only common and domain-specific abbreviations, and must be consistent with these abbreviations. You may abbreviate lexical variables of limited scope in order to avoid overly-long symbol names. + +Line length +▶ +You should format source code so that no line is longer than 100 characters. +Indentation +▶ + +Indent your code the way a properly configured GNU Emacs does. + +Maintain a consistent indentation style throughout a project. + +Indent carefully to make the code easier to understand. + +File Header +▶ + +You should include a description at the top of each source file. + +You should include neither authorship nor copyright information in a source file. + +Vertical white space +▶ +Vertical white space: one blank line between top-level forms. +Horizontal white space +▶ +Horizontal white space: none around parentheses. No tabs. +Documentation +Document everything +▶ +You should use document strings on all visible functions to explain how to use your code. +Comment semicolons +▶ +You must use the appropriate number of semicolons to introduce comments. +Grammar and punctuation +▶ +You should punctuate documentation correctly. +Attention Required +▶ +You must follow the convention of using TODO comments for code requiring special attention. For code using unobvious forms, you must include a comment. +Domain-Specific Languages +▶ +You should document DSLs and any terse program in a DSL. +Naming +Symbol guidelines +▶ +You should use lower case. You should follow the rules for Spelling and Abbreviations You should follow punctuation conventions. +Denote intent, not content +▶ +Name your variables according to their intent, not their content. +Global variables and constants +▶ +Name globals according to convention. +Predicate names +▶ +Names of predicate functions and variables end with a "P". +Omit library prefixes +▶ +You should not include a library or package name as a prefix within the name of symbols. +Packages +▶ +Use packages appropriately. +Language usage guidelines +Mostly Functional Style +▶ +You should avoid side-effects when they are not necessary. +Recursion +▶ +You should favor iteration over recursion. +Special variables +▶ +Use special variables sparingly. +Assignment +▶ +Be consistent in assignment forms. +Assertions and Conditions +▶ +You must make proper usage of assertions and conditions. +Type Checking +▶ +If you know the type of something, you should make it explicit in order to enable compile-time and run-time sanity-checking. +CLOS +▶ +Use CLOS appropriately. +Meta-language guidelines +Macros +▶ +Use macros when appropriate, which is often. Define macros when appropriate, which is seldom. +EVAL-WHEN +▶ +When using EVAL-WHEN, you should almost always use all of (:compile-toplevel :load-toplevel :execute). +Read-time evaluation +▶ +You should use #. sparingly, and you must avoid read-time side-effects. +EVAL +▶ +You must not use EVAL at runtime. +INTERN and UNINTERN +▶ +You must not use INTERN or UNINTERN at runtime. +Data Representation +NIL: empty-list, false and I Don't Know +▶ +Appropriately use or avoid using NIL. +Do not abuse lists +▶ +You must select proper data representation. You must not abuse the LIST data structure. +Lists vs. structures vs. multiple values +▶ +You should use the appropriate representation for product types. +Lists vs. Pairs +▶ +Use the appropriate functions when manipulating lists. +Lists vs. Arrays +▶ +You should use arrays rather than lists where random access matters. +Lists vs. Sets +▶ +You should only use lists as sets for very small lists. +Proper Forms + +You must follow the proper usage regarding well-known functions, macros and special forms. + +Defining Constants +▶ +You must use proper defining forms for constant values. +Defining Functions +▶ +You should make proper use of &OPTIONAL and &KEY arguments. You should not use &AUX arguments. +Conditional Expressions +▶ +Use the appropriate conditional form. +Identity, Equality and Comparisons +▶ +You should use the appropriate predicates when comparing objects. +Iteration +▶ +Use the appropriate form for iteration. +I/O +▶ +Use the appropriate I/O functions. +Optimization +Avoid Allocation +▶ +You should avoid unnecessary allocation of memory. +Unsafe Operations +▶ +You must only use faster unsafe operations when there is a clear performance need and you can document why it's correct. +DYNAMIC-EXTENT +▶ +You should only use DYNAMIC-EXTENT where it matters for performance, and you can document why it is correct. +REDUCE vs APPLY +▶ +You should use REDUCE instead of APPLY where appropriate. +Avoid NCONC +▶ +You should not use NCONC; you should use APPEND instead, or better data structures. +Pitfalls +#'FUN vs. 'FUN +▶ +You should usually refer to a function as #'FUN rather than 'FUN. +Pathnames +▶ +Common Lisp pathnames are tricky. Be aware of pitfalls. Use UIOP. +SATISFIES +▶ +You must be careful when using a SATISFIES clause in a type specifier. +Credits: Adam Worrall, Dan Pierson, Matt Marjanovic, Matt Reklaitis, Paul Weiss, Scott McKay, Sundar Narasimhan, and several other people contributed. Special thanks to Steve Hain, and to the previous editors, in reverse chronological order Dan Weinreb and Jeremy Brown. + +Revision 1.28 + +Robert Brown +François-René Rideau diff --git a/ai/skills/markdown_expert/SKILL.md b/ai/skills/markdown_expert/SKILL.md new file mode 100644 index 0000000..330ed42 --- /dev/null +++ b/ai/skills/markdown_expert/SKILL.md @@ -0,0 +1,17 @@ +--- +name: markdown_expert +description: Google Developer Documentation Style Guide Expert. Use this to ensure all Markdown follow Google's official style guide. +--- +# Markdown Expert + +Expert in Google Developer Documentation Style Guide. + +## Core Mandate +Strictly adhere to the rules and best practices defined in the local [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Guidelines +- **Clear & Concise Writing**: Favor simple sentence structures and active voice. +- **Structural Integrity**: Use hierarchical headings correctly (H1, H2, H3). +- **Semantic Markdown**: Use appropriate elements (lists, code blocks, tables) for clarity. +- **Google Style**: Follow the local style guide precisely for all formatting and wording. +- **Alerts & Callouts**: Use GitHub-style alerts (`> [!NOTE]`, `> [!IMPORTANT]`, etc.). diff --git a/ai/skills/markdown_expert/STYLE_GUIDE.md b/ai/skills/markdown_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..609102f --- /dev/null +++ b/ai/skills/markdown_expert/STYLE_GUIDE.md @@ -0,0 +1,650 @@ +styleguide +Markdown style guide + +Much of what makes Markdown refreshing is the ability to write plain text and get great formatted output as a result. To keep the slate clean for the next author, your Markdown should be simple and consistent with the whole corpus wherever possible. + +We seek to balance three goals: + +Source text is readable and portable. +The Markdown corpus is maintainable over time and across teams. +The syntax is simple and easy to remember. + +Contents: + +Minimum viable documentation +Better is better than best +Capitalization +Document layout +Table of contents +Character line limit +Trailing whitespace +Headings +ATX-style headings +Use unique, complete names for headings +Add spacing to headings +Use a single H1 heading +Capitalization of titles and headers +Lists +Use lazy numbering for long lists +Nested list spacing +Code +Inline +Use code span for escaping +Codeblocks +Declare the language +Escape newlines +Use fenced code blocks instead of indented code blocks +Nest codeblocks within lists +Links +Use explicit paths for links within Markdown +Avoid relative paths unless within the same directory +Use informative Markdown link titles +Reference links +Use reference links for long links +Use reference links to reduce duplication +Define reference links after their first use +Images +Tables +Strongly prefer Markdown to HTML +Minimum viable documentation + +A small set of fresh and accurate docs is better than a sprawling, loose assembly of “documentation” in various states of disrepair. + +The Markdown way encourages engineers to take ownership of their docs and keep them up to date with the same zeal we keep our tests in good order. Strive for this. + +Identify what you really need: release docs, API docs, testing guidelines. +Delete cruft frequently and in small batches. +Better is better than best + +The standards for an internal documentation review are different from the standards for code reviews. Reviewers should ask for improvements, but in general, the author should always be able to invoke the “Better/Best Rule.” + +Fast iteration is your friend. To get long-term improvement, authors must stay productive when making short-term improvements. Set lower standards for each CL, so that more such CLs can happen. + +As a reviewer of a documentation CL: + +When reasonable, LGTM immediately and trust that comments will be fixed appropriately. +Prefer to suggest an alternative rather than leaving a vague comment. +For substantial changes, start your own follow-up CL instead. Especially try to avoid comments of the form “You should also…”. +On rare occasions, hold up submission if the CL actually makes the docs worse. It’s okay to ask the author to revert. + +As an author: + +Avoid wasting cycles with trivial argument. Capitulate early and move on. +Cite the Better/Best Rule as often as needed. +Capitalization + +Use the original names of products, tools and binaries, preserving the capitalization. E.g.: + +# Markdown style guide + +`Markdown` is a dead-simple platform for internal engineering documentation. + + +and not + +# markdown bad style guide example + +`markdown` is a dead-simple platform for internal engineering documentation. + +Document layout + +In general, documents benefit from some variation of the following layout: + +# Document Title + +Short introduction. + +[TOC] + +## Topic + +Content. + +## See also + +* https://link-to-more-info + + +# Document title: The first heading should be a level-one heading, ideally the same or nearly the same as the filename. The first level-one heading is used as the page . + +author: Optional. If you’d like to claim ownership of the document or if you are very proud of it, add yourself under the title. However, revision history generally suffices. + +Short introduction. 1–3 sentences providing a high-level overview of the topic. Imagine yourself as a complete newbie who landed on your “Extending Foo” doc and doesn’t know the most basic information you take for granted. “What is Foo? Why would I extend it?” + +[TOC]: if you use hosting that supports table of contents, such as Gitiles, put [TOC] after the short introduction. See [TOC] documentation. + +## Topic: The rest of your headings should start from level 2. + +## See also: Put miscellaneous links at the bottom for the user who wants to know more or didn’t find what they needed. + +Table of contents +Use a [TOC] directive + +Use a [TOC] directive unless all of your content is above the fold1 on a laptop. + +Place the [TOC] directive after the introduction + +Place the [TOC] directive after your page’s introduction and before the first H2 heading. For example: + +# My Page + +This is my introduction **before** the TOC. + +[TOC] + +## My first H2 + +# My Page + +[TOC] + +This is my introduction **after** the TOC where it should not be. + +## My first H2 + + +For users who read your documentation visually, it doesn’t matter where your [TOC] directive is placed, as Markdown always displays the TOC toward the top and to the right of the page. However, [TOC] placement matters a lot when screen readers or keyboard controls are involved. + +That’s because [TOC] inserts the HTML for the table of contents into the DOM wherever you’ve included the directive in your Markdown file. If, for example, you place the directive at the very bottom of your file, screen readers won’t read it until they get to the end of the document. + +Character line limit + +Markdown content follows the residual convention of an 80-character line limit. Why? Because it’s what most of us do for code. + +Tooling integration: All our tooling is designed around code, so the more our documents are formatted according to similar rules, the better these tools will work. For example, Code Search doesn’t soft wrap. + +Quality. The more engineers use their well-worn coding habits when creating and editing Markdown content, the better the quality. Markdown takes advantage of the excellent review culture we already have. + +Exceptions + +Exceptions to the 80-character rule include: + +Links +Tables +Headings +Code blocks + +This means that lines with links are allowed to extend past column 80, along with any relevant punctuation: + +* See the + [foo docs](https://gerrit.googlesource.com/gitiles/+/HEAD/Documentation/markdown.md). + and find the logfile. + + +However, note that text before and after the link gets wrapped. + +Tables may also run long. However, there are best practices for creating short, readable tables. + +Foo | Bar | Baz +----------------------------------------------------------------------------- | --- | --- +Somehow-unavoidable-long-cell-filled-with-content-that-simply-refuses-to-wrap | Foo | Bar + +Trailing whitespace + +Don’t use trailing whitespace. Use a trailing backslash to break lines. + +The CommonMark spec decrees that two spaces at the end of a line should insert a <br /> tag. However, many directories have a presubmit check for trailing whitespace, and many IDEs will clean it up anyway. + +Use a trailing backslash, sparingly: + +For some reason I just really want a break here,\ +though it's probably not necessary. + + +Best practice is to avoid the need for a <br /> altogether. A pair of newlines will create a paragraph tag; get used to that. + +Headings +ATX-style headings +# Heading 1 + +## Heading 2 + + +Headings with = or - underlines can be annoying to maintain and don’t fit with the rest of the heading syntax. An editor has to ask: Does --- mean H1 or H2? + +Heading - do you remember what level? DO NOT DO THIS. +--------- + +Use unique, complete names for headings + +Use unique and fully descriptive names for each heading, even for sub-sections. Since link anchors are constructed from headings, this helps ensure that the automatically-constructed anchor links are intuitive and clear. + +For example, instead of: + +## Foo +### Summary +### Example +## Bar +### Summary +### Example + + +prefer: + +## Foo +### Foo summary +### Foo example +## Bar +### Bar summary +### Bar example + +Add spacing to headings + +Prefer spacing after # and newlines before and after: + +...text before. + +## Heading 2 + +Text after... + + +Lack of spacing makes it a little harder to read in source: + +...text before. + +##Heading 2 +Text after... DO NOT DO THIS. + +Use a single H1 heading + +Use one H1 heading as the title of your document. Subsequent headings should be H2 or deeper. See Document layout for more information. + +Capitalization of titles and headers + +Follow the guidance for capitalization in the Google Developer Documentation Style Guide. + +Lists +Use lazy numbering for long lists + +Markdown is smart enough to let the resulting HTML render your numbered lists correctly. For longer lists that may change, especially long nested lists, use “lazy” numbering: + +1. Foo. +1. Bar. + 1. Foofoo. + 1. Barbar. +1. Baz. + + +However, if the list is small and you don’t anticipate changing it, prefer fully numbered lists, because it’s nicer to read in source: + +1. Foo. +2. Bar. +3. Baz. + +Nested list spacing + +When nesting lists, use a 4-space indent for both numbered and bulleted lists: + +1. Use 2 spaces after the item number, so the text itself is indented 4 spaces. + Use a 4-space indent for wrapped text. +2. Use 2 spaces again for the next item. + +* Use 3 spaces after a bullet, so the text itself is indented 4 spaces. + Use a 4-space indent for wrapped text. + 1. Use 2 spaces with numbered lists, as before. + Wrapped text in a nested list needs an 8-space indent. + 2. Looks nice, doesn't it? +* Back to the bulleted list, indented 3 spaces. + + +The following works, but it’s very messy: + +* One space, +with no indent for wrapped text. + 1. Irregular nesting... DO NOT DO THIS. + + +Even when there’s no nesting, using the 4 space indent makes layout consistent for wrapped text: + +* Foo, + wrapped with a 4-space indent. + +1. Two spaces for the list item + and 4 spaces before wrapped text. +2. Back to 2 spaces. + + +However, when lists are small, not nested, and a single line, one space can suffice for both kinds of lists: + +* Foo +* Bar +* Baz. + +1. Foo. +2. Bar. + +Code +Inline + +`Backticks` designate inline code that will be rendered literally. Use them for short code quotations, field names, and more: + +You'll want to run `really_cool_script.sh arg`. + +Pay attention to the `foo_bar_whammy` field in that table. + + +Use inline code when referring to file types in a generic sense, rather than a specific existing file: + +Be sure to update your `README.md`! + +Use code span for escaping + +When you don’t want text to be processed as normal Markdown, like a fake path or example URL that would lead to a bad autolink, wrap it in backticks: + +An example Markdown shortlink would be: `Markdown/foo/Markdown/bar.md` + +An example query might be: `https://www.google.com/search?q=$TERM` + +Codeblocks + +For code quotations longer than a single line, use a fenced code block: + +```python +def Foo(self, bar): + self.bar = bar +``` + +Declare the language + +It is best practice to explicitly declare the language, so that neither the syntax highlighter nor the next editor must guess. + +Use fenced code blocks instead of indented code blocks + +Four-space indenting is also interpreted as a code block. However, we strongly recommend fencing for all code blocks. + +Indented code blocks can sometimes look cleaner in the source, but they have several drawbacks: + +You cannot specify the language. Some Markdown features are tied to language specifiers. +The beginning and end of the code block are ambiguous. +Indented code blocks are harder to search for in Code Search. +You'll need to run: + + bazel run :thing -- --foo + +And then: + + bazel run :another_thing -- --bar + +And again: + + bazel run :yet_again -- --baz + +Escape newlines + +Because most command-line snippets are intended to be copied and pasted directly into a terminal, it’s best practice to escape any newlines. Use a single backslash at the end of the line: + +```shell +$ bazel run :target -- --flag --foo=longlonglonglonglongvalue \ + --bar=anotherlonglonglonglonglonglonglonglonglonglongvalue +``` + +Nest codeblocks within lists + +If you need a code block within a list, make sure to indent it so as to not break the list: + +* Bullet. + + ```c++ + int foo; + ``` + +* Next bullet. + + +You can also create a nested code block with 4 spaces. Simply indent 4 additional spaces from the list indentation: + +* Bullet. + + int foo; + +* Next bullet. + +Links + +Long links make source Markdown difficult to read and break the 80 character wrapping. Wherever possible, shorten your links. + +Use explicit paths for links within Markdown + +Use the explicit path for Markdown links. For example: + +[...](/path/to/other/markdown/page.md) + + +You don’t need to use the entire qualified URL: + +[...](https://bad-full-url.example.com/path/to/other/markdown/page.md) + +Avoid relative paths unless within the same directory + +Relative paths are fairly safe within the same directory. For example: + +[...](other-page-in-same-dir.md) +[...](/path/to/another/dir/other-page.md) + + +Avoid relative links if you need to specify other directories with ../: + +[...](../../bad/path/to/another/dir/other-page.md) + +Use informative Markdown link titles + +Markdown link syntax allows you to set a link title. Use it wisely. Users often do not read documents; they scan them. + +Links catch the eye. But titling your links “here,” “link,” or simply duplicating the target URL tells the hasty reader precisely nothing and is a waste of space: + +DO NOT DO THIS. + +See the Markdown guide for more info: [link](markdown.md), or check out the +style guide [here](/styleguide/docguide/style.html). + +Check out a typical test result: +[https://example.com/foo/bar](https://example.com/foo/bar). + + +Instead, write the sentence naturally, then go back and wrap the most appropriate phrase with the link: + +See the [Markdown guide](markdown.md) for more info, or check out the +[style guide](/styleguide/docguide/style.html). + +Check out a +[typical test result](https://example.com/foo/bar). + +Reference + +For long links or image URLs, you may want to split the link use from the link definition, like this: + +See the [Markdown style guide][style], which has suggestions for making docs more +readable. + +[style]: http://Markdown/corp/Markdown/docs/reference/style.md + +Use reference links for long links + +Use reference links where the length of the link would detract from the readability of the surrounding text if it were inlined. Reference links make it harder to see the destination of a link in source text, and add additional syntax. + +In this example, reference link usage is not appropriate, because the link is not long enough to disrupt the flow of the text: + +DO NOT DO THIS. + +The [style guide][style_guide] says not to use reference links unless you have +to. + +[style_guide]: https://google.com/Markdown-style + + +Just inline it instead: + +https://google.com/Markdown-style says not to use reference links unless you have to. + + +In this example, the link destination is long enough that it makes sense to use a reference link: + +The [style guide] says not to use reference links unless you have to. + +[style guide]: https://docs.google.com/document/d/13HQBxfhCwx8lVRuN2Wf6poqvAfVeEXmFVcawP5I6B3c/edit + + +Use reference links more often in tables. It is particularly important to keep table content short, since Markdown does not provide a facility to break text in cell tables across multiple lines, and smaller tables are more readable. + +For example, this table’s readability is worsened by inline links: + +DO NOT DO THIS. + +Site | Description +---------------------------------------------------------------- | ----------------------- +[site 1](http://google.com/excessively/long/path/example_site_1) | This is example site 1. +[site 2](http://google.com/excessively/long/path/example_site_2) | This is example site 2. + + +Instead, use reference links to keep the line length manageable: + +Site | Description +-------- | ----------------------- +[site 1] | This is example site 1. +[site 2] | This is example site 2. + +[site 1]: http://google.com/excessively/long/path/example_site_1 +[site 2]: http://google.com/excessively/long/path/example_site_2 + +Use reference links to reduce duplication + +Consider using reference links when referencing the same link destination multiple times in a document, to reduce duplication. + +Define reference links after their first use + +We recommend putting reference link definitions just before the next heading, at the end of the section in which they’re first used. If your editor has its own opinion about where they should go, don’t fight it; the tools always win. + +We define a “section” as all text between two headings. Think of reference links like footnotes, and the current section like the current page. + +This arrangement makes it easy to find the link destination in source view, while keeping the flow of text free from clutter. In long documents with lots of reference links, it also prevents “footnote overload” at the bottom of the file, which makes it difficult to pick out the relevant link destination. + +There is one exception to this rule: reference link definitions that are used in multiple sections should go at the end of the document. This avoids dangling links when a section is updated or moved. + +In the following example, the reference definition is far from its initial use, which makes the document harder to read: + +# Header FOR A BAD DOCUMENT + +Some text with a [link][link_def]. + +Some more text with the same [link][link_def]. + +## Header 2 + +... lots of text ... + +## Header 3 + +Some more text using a [different_link][different_link_def]. + +[link_def]: http://reallyreallyreallylonglink.com +[different_link_def]: http://differentreallyreallylonglink.com + + +Instead, put it just before the header following its first use: + +# Header + +Some text with a [link][link_def]. + +Some more text with the same [link][link_def]. + +[link_def]: http://reallyreallyreallylonglink.com + +## Header 2 + +... lots of text ... + +## Header 3 + +Some more text using a [different_link][different_link_def]. + +[different_link_def]: http://differentreallyreallylonglink.com + +Images + +See image syntax. + +Use images sparingly, and prefer simple screenshots. This guide is designed around the idea that plain text gets users down to the business of communication faster with less reader distraction and author procrastination. However, it’s sometimes very helpful to show what you mean. + +Use images when it’s easier to show a reader something than to describe it. For example, explaining how to navigate a UI is often easier with an image than text. +Make sure to provide appropriate text to describe your image. Readers who are not sighted cannot see your image and still need to understand the content! See the alt text best practices below. +Tables + +Use tables when they make sense: for the presentation of tabular data that needs to be scanned quickly. + +Avoid using tables when your data could easily be presented in a list. Lists are much easier to write and read in Markdown. + +For example: + +DO NOT DO THIS + +Fruit | Metrics | Grows on | Acute curvature | Attributes | Notes +------ | ------------ | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- +Apple | Very popular | Trees | | [Juicy](http://cs/SomeReallyReallyReallyReallyReallyReallyReallyReallyLongQuery), Firm, Sweet | Apples keep doctors away. +Banana | Very popular | Trees | 16 degrees average | [Convenient](http://cs/SomeDifferentReallyReallyReallyReallyReallyReallyReallyReallyLongQuery), Soft, Sweet | Contrary to popular belief, most apes prefer mangoes. Don't you? See the [design doc][banana_v2] for the newest hotness in bananiels. + + +This table illustrates a few typical problems: + +Poor distribution: Several columns don’t differ across rows, and some cells are empty. This is usually a sign that your data may not benefit from tabular display. + +Unbalanced dimensions: There are a small number of rows relative to columns. When this ratio is unbalanced in either direction, a table becomes little more than an inflexible format for text. + +Rambling prose in some cells. Tables should tell a succinct story at a glance. + +Lists and subheadings sometimes suffice to present the same information. Let’s see this data in list form: + +## Fruits + +Both types are highly popular, sweet, and grow on trees. + +### Apple + +* [Juicy](http://SomeReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongURL) +* Firm + +Apples keep doctors away. + +### Banana + +* [Convenient](http://cs/SomeDifferentReallyReallyReallyReallyReallyReallyReallyReallyLongQuery) +* Soft +* 16 degrees average acute curvature. + +Contrary to popular belief, most apes prefer mangoes. Don't you? + +See the [design doc][banana_v2] for the newest hotness in bananiels. + + +The list form is more spacious, and arguably therefore much easier for the reader to find what interests her in this case. + +However, there are times a table is the best choice. When you have: + +Relatively uniform data distribution across two dimensions. +Many parallel items with distinct attributes. + +In those cases, a table format is just the thing. In fact, a compact table can improve readability: + +Transport | Favored by | Advantages +---------------- | -------------- | ----------------------------------------------- +Swallow | Coconuts | [Fast when unladen][airspeed] +Bicycle | Miss Gulch | [Weatherproof][tornado_proofing] +X-34 landspeeder | Whiny farmboys | [Cheap][tosche_station] since the XP-38 came out + +[airspeed]: http://google3/airspeed.h +[tornado_proofing]: http://google3/kansas/ +[tosche_station]: http://google3/power_converter.h + + +Note that reference links are used to keep the table cells manageable. + +Strongly prefer Markdown to HTML + +Please prefer standard Markdown syntax wherever possible and avoid HTML hacks. If you can’t seem to accomplish what you want, reconsider whether you really need it. Except for big tables, Markdown meets almost all needs already. + +Every bit of HTML hacking reduces the readability and portability of our Markdown corpus. This in turn limits the usefulness of integrations with other tools, which may either present the source as plain text or render it. See Philosophy. + +Gitiles does not render HTML. + +Content is “above the fold” if it is visible when the page is first displayed. Content is “below the fold” if it is hidden until the user scrolls down the page on a computer or literally unfolds a document such as a newspaper. ↩ diff --git a/ai/skills/shell_expert/SKILL.md b/ai/skills/shell_expert/SKILL.md new file mode 100644 index 0000000..72da4c5 --- /dev/null +++ b/ai/skills/shell_expert/SKILL.md @@ -0,0 +1,16 @@ +--- +name: shell_expert +description: Google Shell Style Guide Expert. For robust and maintainable bash/sh scripts. +--- +# Shell Expert + +Expert in Google Shell Style Guide. + +## Core Mandate +Strictly follow [STYLE_GUIDE.md](./STYLE_GUIDE.md). + +## Key Principles +- Always quote variables. +- Use [[ for testing. +- Prefer over backticks. +- Proper error handling (set -e). diff --git a/ai/skills/shell_expert/STYLE_GUIDE.md b/ai/skills/shell_expert/STYLE_GUIDE.md new file mode 100644 index 0000000..bb1bb4d --- /dev/null +++ b/ai/skills/shell_expert/STYLE_GUIDE.md @@ -0,0 +1,948 @@ +styleguide +Shell Style Guide + +Authored, revised and maintained by many Googlers. + +Table of Contents +Section Contents +Background Which Shell to Use - When to use Shell +Shell Files and Interpreter Invocation File Extensions - SUID/SGID +Environment STDOUT vs STDERR +Comments File Header - Function Comments - Implementation Comments - TODO Comments +Formatting Indentation - Line Length and Long Strings - Pipelines - Control Flow - Case statement - Variable expansion - Quoting +Features and Bugs ShellCheck - Command Substitution - Test, [… ], and [[… ]] - Testing Strings - Wildcard Expansion of Filenames - Eval - Arrays - Pipes to While - Arithmetic - Aliases +Naming Conventions Function Names - Variable Names - Constants and Environment Variable Names - Source Filenames - Use Local Variables - Function Location - main +Calling Commands Checking Return Values - Builtin Commands vs. External Commands +When in Doubt: Be Consistent   + +Background + +Which Shell to Use + +Bash is the only shell scripting language permitted for executables. + +Executables must start with #!/bin/bash and minimal flags. Use set to set shell options so that calling your script as bash script_name does not break its functionality. + +Restricting all executable shell scripts to bash gives us a consistent shell language that’s installed on all our machines. In particular, this means there is generally no need to strive for POSIX-compatibility or otherwise avoid “bashisms”. + +The only exception to the above is where you’re forced to by whatever you’re coding for. For example some legacy operating systems or constrained execution environments may require plain Bourne shell for certain scripts. + +When to use Shell + +Shell should only be used for small utilities or simple wrapper scripts. + +While shell scripting isn’t a development language, it is used for writing various utility scripts throughout Google. This style guide is more a recognition of its use rather than a suggestion that it be used for widespread deployment. + +Some guidelines: + +If you’re mostly calling other utilities and are doing relatively little data manipulation, shell is an acceptable choice for the task. +If performance matters, use something other than shell. +If you are writing a script that is more than 100 lines long, or that uses non-straightforward control flow logic, you should rewrite it in a more structured language now. Bear in mind that scripts grow. Rewrite your script early to avoid a more time-consuming rewrite at a later date. +When assessing the complexity of your code (e.g. to decide whether to switch languages) consider whether the code is easily maintainable by people other than its author. + +Shell Files and Interpreter Invocation + +File Extensions + +Executables should have a .sh extension or no extension. + +If the executable will have a build rule that renames the source file then prefer to use a .sh extension. This enables you to use the recommended naming convention, with a source file like foo.sh and a build rule named foo. +If the executable will be added directly to the user’s PATH, then prefer to use no extension. It is not necessary to know what language a program is written in when executing it and shell doesn’t require an extension so we prefer not to use one for executables that will be directly invoked by users. At the same time, consider whether it is preferable to deploy the output of a build rule rather than deploying the source file directly. +If neither of the above apply, then either choice is acceptable. + +Libraries must have a .sh extension and should not be executable. + +SUID/SGID + +SUID and SGID are forbidden on shell scripts. + +There are too many security issues with shell that make it nearly impossible to secure sufficiently to allow SUID/SGID. While bash does make it difficult to run SUID, it’s still possible on some platforms which is why we’re being explicit about banning it. + +Use sudo to provide elevated access if you need it. + +Environment + +STDOUT vs STDERR + +All error messages should go to STDERR. + +This makes it easier to separate normal status from actual issues. + +A function to print out error messages along with other status information is recommended. + +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +if ! do_something; then + err "Unable to do_something" + exit 1 +fi + + +Comments + +File Header + +Start each file with a description of its contents. + +Every file must have a top-level comment including a brief overview of its contents. A copyright notice and author information are optional. + +Example: + +#!/bin/bash +# +# Perform hot backups of Oracle databases. + + +Function Comments + +Any function that is not both obvious and short must have a function header comment. Any function in a library must have a function header comment regardless of length or complexity. + +It should be possible for someone else to learn how to use your program or to use a function in your library by reading the comments (and self-help, if provided) without reading the code. + +All function header comments should describe the intended API behaviour using: + +Description of the function. +Globals: List of global variables used and modified. +Arguments: Arguments taken. +Outputs: Output to STDOUT or STDERR. +Returns: Returned values other than the default exit status of the last command run. + +Example: + +####################################### +# Cleanup files from the backup directory. +# Globals: +# BACKUP_DIR +# ORACLE_SID +# Arguments: +# None +####################################### +function cleanup() { + … +} + +####################################### +# Get configuration directory. +# Globals: +# SOMEDIR +# Arguments: +# None +# Outputs: +# Writes location to stdout +####################################### +function get_dir() { + echo "${SOMEDIR}" +} + +####################################### +# Delete a file in a sophisticated manner. +# Arguments: +# File to delete, a path. +# Returns: +# 0 if thing was deleted, non-zero on error. +####################################### +function del_thing() { + rm "$1" +} + + +Implementation Comments + +Comment tricky, non-obvious, interesting or important parts of your code. + +This follows general Google coding comment practice. Don’t comment everything. If there’s a complex algorithm or you’re doing something out of the ordinary, put a short comment in. + +TODO Comments + +Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect. + +This matches the convention in the C++ Guide. + +TODOs should include the string TODO in all caps, followed by the name, e-mail address, or other identifier of the person with the best context about the problem referenced by the TODO. The main purpose is to have a consistent TODO that can be searched to find out how to get more details upon request. A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO, it is almost always your name that is given. + +Examples: + +# TODO(mrmonkey): Handle the unlikely edge cases (bug ####) + + +Formatting + +While you should follow the style that’s already there for files that you’re modifying, the following are required for any new code. + +Indentation + +Indent 2 spaces. No tabs. + +Use blank lines between blocks to improve readability. Indentation is two spaces. Whatever you do, don’t use tabs. For existing files, stay faithful to the existing indentation. + +Exception: The only exception for using tabs is for the body of <<- tab-indented here-document. + +Line Length and Long Strings + +Maximum line length is 80 characters. + +If you have to write literal strings that are longer than 80 characters, this should be done with a here document or an embedded newline if possible. + +Words that are longer than 80 chars and can’t sensibly be split are ok, but where possible these items should be on a line of their own, or factored into a variable. Examples include file paths and URLs, particularly where string-matching them (such as grep) is valuable for maintenance. + +# DO use 'here document's +cat <<END +I am an exceptionally long +string. +END + +# Embedded newlines are ok too +long_string="I am an exceptionally +long string." + +long_file="/i/am/an/exceptionally/loooooooooooooooooooooooooooooooooooooooooooooooooooong_file" + +long_string_with_long_file="i am including an exceptionally \ +/very/long/file\ + in this long string." + +# Long file converted into a shorter variable name with cleaner line breaking. +long_string_alt="i am an including an exceptionally ${long_file} in this long\ + string" + +# Just because a line contains an exception doesn't mean the rest of the +# line shouldn't be wrapped like usual. + +bad_long_string_with_long_file="i am including an exceptionally /very/long/file in this long string." + + +Pipelines + +Pipelines should be split one per line if they don’t all fit on one line. + +If a pipeline all fits on one line, it should be on one line. + +If not, it should be split at one pipe segment per line with the pipe on the newline and a 2 space indent for the next section of the pipe. \ should be consistently used to indicate line continuation. This applies to a chain of commands combined using | as well as to logical compounds using || and &&. + +# All fits on one line +command1 | command2 + +# Long commands +command1 \ + | command2 \ + | command3 \ + | command4 + + +This helps readability when distinguishing a pipeline from a regular long command continuation, particularly if the line is using both. + +Comments will need to precede the whole pipeline. If the comment and pipeline are large and complex, then it is worth considering moving low level details of them aside by using a helper function. + +Control Flow + +Put ; then and ; do on the same line as the if, for, or while. + +Control flow statements in shell are a bit different, but we follow the same principles as with braces when declaring functions. That is: ; then and ; do should be on the same line as the if/for/while/until/select. else should be on its own line and closing statements (fi and done) should be on their own line vertically aligned with the opening statement. + +Example: + +# If inside a function remember to declare the loop variable as +# a local to avoid it leaking into the global environment: +local dir +for dir in "${dirs_to_cleanup[@]}"; do + if [[ -d "${dir}/${SESSION_ID}" ]]; then + log_date "Cleaning up old files in ${dir}/${SESSION_ID}" + rm "${dir}/${SESSION_ID}/"* || error_message + else + mkdir -p "${dir}/${SESSION_ID}" || error_message + fi +done + + +Although it is possible to omit in "$@" in for loops we recommend consistently including it for clarity. + +for arg in "$@"; do + echo "argument: ${arg}" +done + + +Case statement +Indent alternatives by 2 spaces. +A one-line alternative needs a space after the close parenthesis of the pattern and before the ;;. +Long or multi-command alternatives should be split over multiple lines with the pattern, actions, and ;; on separate lines. + +The matching expressions are indented one level from the case and esac. Multiline actions are indented another level. In general, there is no need to quote match expressions. Pattern expressions should not be preceded by an open parenthesis. Avoid the ;& and ;;& notations. + +case "${expression}" in + a) + variable="…" + some_command "${variable}" "${other_expr}" … + ;; + absolute) + actions="relative" + another_command "${actions}" "${other_expr}" … + ;; + *) + error "Unexpected expression '${expression}'" + ;; +esac + + +Simple commands may be put on the same line as the pattern and ;; as long as the expression remains readable. This is often appropriate for single-letter option processing. When the actions don’t fit on a single line, put the pattern on a line on its own, then the actions, then ;; also on a line of its own. When on the same line as the actions, use a space after the close parenthesis of the pattern and another before the ;;. + +verbose='false' +aflag='' +bflag='' +files='' +while getopts 'abf:v' flag; do + case "${flag}" in + a) aflag='true' ;; + b) bflag='true' ;; + f) files="${OPTARG}" ;; + v) verbose='true' ;; + *) error "Unexpected option ${flag}" ;; + esac +done + + +Variable expansion + +In order of precedence: Stay consistent with what you find; quote your variables; prefer "${var}" over "$var". + +These are strongly recommended guidelines but not mandatory regulation. Nonetheless, the fact that it’s a recommendation and not mandatory doesn’t mean it should be taken lightly or downplayed. + +They are listed in order of precedence. + +Stay consistent with what you find for existing code. +Quote variables, see Quoting section below. +Don’t brace-delimit single character shell specials / positional parameters, unless strictly necessary or avoiding deep confusion. + +Prefer brace-delimiting all other variables. + +# Section of *recommended* cases. + +# Preferred style for 'special' variables: +echo "Positional: $1" "$5" "$3" +echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ …" + +# Braces necessary: +echo "many parameters: ${10}" + +# Braces avoiding confusion: +# Output is "a0b0c0" +set -- a b c +echo "${1}0${2}0${3}0" + +# Preferred style for other variables: +echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}" +while read -r f; do + echo "file=${f}" +done < <(find /tmp) + +# Section of *discouraged* cases + +# Unquoted vars, unbraced vars, brace-delimited single letter +# shell specials. +echo a=$avar "b=$bvar" "PID=${$}" "${1}" + +# Confusing use: this is expanded as "${1}0${2}0${3}0", +# not "${10}${20}${30} +set -- a b c +echo "$10$20$30" + + +NOTE: Using braces in ${var} is not a form of quoting. “Double quotes” must be used as well. + +Quoting +Always quote strings containing variables, command substitutions, spaces or shell meta characters, unless careful unquoted expansion is required or it’s a shell-internal integer (see next point). +Use arrays for safe quoting of lists of elements, especially command-line flags. See Arrays below. +Optionally quote shell-internal, readonly special variables that are defined to be integers: $?, $#, $$, $!. Prefer quoting of “named” internal integer variables, e.g. PPID etc for consistency. +Prefer quoting strings that are “words” (as opposed to command options or path names). +Be aware of the quoting rules for pattern matches in [[ … ]]. See the Test, [ … ], and [[ … ]] section below. +Use "$@" unless you have a specific reason to use $*, such as simply appending the arguments to a string in a message or log. +# 'Single' quotes indicate that no substitution is desired. +# "Double" quotes indicate that substitution is required/tolerated. + +# Simple examples + +# "quote command substitutions" +# Note that quotes nested inside "$()" don't need escaping. +flag="$(some_command and its args "$@" 'quoted separately')" + +# "quote variables" +echo "${flag}" + +# Use arrays with quoted expansion for lists. +declare -a FLAGS +FLAGS=( --foo --bar='baz' ) +readonly FLAGS +mybinary "${FLAGS[@]}" + +# It's ok to not quote internal integer variables. +if (( $# > 3 )); then + echo "ppid=${PPID}" +fi + +# "never quote literal integers" +value=32 +# "quote command substitutions", even when you expect integers +number="$(generate_number)" + +# "prefer quoting words", not compulsory +readonly USE_INTEGER='true' + +# "quote shell meta characters" +echo 'Hello stranger, and well met. Earn lots of $$$' +echo "Process $$: Done making \$\$\$." + +# "command options or path names" +# ($1 is assumed to contain a value here) +grep -li Hugo /dev/null "$1" + +# Less simple examples +# "quote variables, unless proven false": ccs might be empty +git send-email --to "${reviewers}" ${ccs:+"--cc" "${ccs}"} + +# Positional parameter precautions: $1 might be unset +# Single quotes leave regex as-is. +grep -cP '([Ss]pecial|\|?characters*)$' ${1:+"$1"} + +# For passing on arguments, +# "$@" is right almost every time, and +# $* is wrong almost every time: +# +# * $* and $@ will split on spaces, clobbering up arguments +# that contain spaces and dropping empty strings; +# * "$@" will retain arguments as-is, so no args +# provided will result in no args being passed on; +# This is in most cases what you want to use for passing +# on arguments. +# * "$*" expands to one argument, with all args joined +# by (usually) spaces, +# so no args provided will result in one empty string +# being passed on. +# +# Consult +# https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html and +# https://mywiki.wooledge.org/BashGuide/Arrays for more + +(set -- 1 "2 two" "3 three tres"; echo $#; set -- "$*"; echo "$#, $@") +(set -- 1 "2 two" "3 three tres"; echo $#; set -- "$@"; echo "$#, $@") + + +Features and Bugs + +ShellCheck + +The ShellCheck project identifies common bugs and warnings for your shell scripts. It is recommended for all scripts, large or small. + +Command Substitution + +Use $(command) instead of backticks. + +Nested backticks require escaping the inner ones with \ . The $(command) format doesn’t change when nested and is easier to read. + +Example: + +# This is preferred: +var="$(command "$(command1)")" + +# This is not: +var="`command \`command1\``" + + +Test, [ … ], and [[ … ]] + +[[ … ]] is preferred over [ … ], test and /usr/bin/[. + +[[ … ]] reduces errors as no pathname expansion or word splitting takes place between [[ and ]]. In addition, [[ … ]] allows for pattern and regular expression matching, while [ … ] does not. + +# This ensures the string on the left is made up of characters in +# the alnum character class followed by the string name. +# Note that the RHS should not be quoted here. +if [[ "filename" =~ ^[[:alnum:]]+name ]]; then + echo "Match" +fi + +# This matches the exact pattern "f*" (Does not match in this case) +if [[ "filename" == "f*" ]]; then + echo "Match" +fi + +# This gives a "too many arguments" error as f* is expanded to the +# contents of the current directory. It might also trigger the +# "unexpected operator" error because `[` does not support `==`, only `=`. +if [ "filename" == f* ]; then + echo "Match" +fi + + +For the gory details, see E14 in the Bash FAQ + +Testing Strings + +Use quotes rather than filler characters where possible. + +Bash is smart enough to deal with an empty string in a test. So, given that the code is much easier to read, use tests for empty/non-empty strings or empty strings rather than filler characters. + +# Do this: +if [[ "${my_var}" == "some_string" ]]; then + do_something +fi + +# -z (string length is zero) and -n (string length is not zero) are +# preferred over testing for an empty string +if [[ -z "${my_var}" ]]; then + do_something +fi + +# This is OK (ensure quotes on the empty side), but not preferred: +if [[ "${my_var}" == "" ]]; then + do_something +fi + +# Not this: +if [[ "${my_var}X" == "some_stringX" ]]; then + do_something +fi + + +To avoid confusion about what you’re testing for, explicitly use -z or -n. + +# Use this +if [[ -n "${my_var}" ]]; then + do_something +fi + +# Instead of this +if [[ "${my_var}" ]]; then + do_something +fi + + +For clarity, use == for equality rather than = even though both work. The former encourages the use of [[ and the latter can be confused with an assignment. However, be careful when using < and > in [[ … ]] which performs a lexicographical comparison. Use (( … )) or -lt and -gt for numerical comparison. + +# Use this +if [[ "${my_var}" == "val" ]]; then + do_something +fi + +if (( my_var > 3 )); then + do_something +fi + +if [[ "${my_var}" -gt 3 ]]; then + do_something +fi + +# Instead of this +if [[ "${my_var}" = "val" ]]; then + do_something +fi + +# Probably unintended lexicographical comparison. +if [[ "${my_var}" > 3 ]]; then + # True for 4, false for 22. + do_something +fi + + +Wildcard Expansion of Filenames + +Use an explicit path when doing wildcard expansion of filenames. + +As filenames can begin with a -, it’s a lot safer to expand wildcards with ./* instead of *. + +# Here's the contents of the directory: +# -f -r somedir somefile + +# Incorrectly deletes almost everything in the directory by force +psa@bilby$ rm -v * +removed directory: `somedir' +removed `somefile' + +# As opposed to: +psa@bilby$ rm -v ./* +removed `./-f' +removed `./-r' +rm: cannot remove `./somedir': Is a directory +removed `./somefile' + + +Eval + +eval should be avoided. + +Eval munges the input when used for assignment to variables and can set variables without making it possible to check what those variables were. + +# What does this set? +# Did it succeed? In part or whole? +eval $(set_my_variables) + +# What happens if one of the returned values has a space in it? +variable="$(eval some_function)" + + +Arrays + +Bash arrays should be used to store lists of elements, to avoid quoting complications. This particularly applies to argument lists. Arrays should not be used to facilitate more complex data structures (see When to use Shell above). + +Arrays store an ordered collection of strings, and can be safely expanded into individual elements for a command or loop. + +Using a single string for multiple command arguments should be avoided, as it inevitably leads to authors using eval or trying to nest quotes inside the string, which does not give reliable or readable results and leads to needless complexity. + +# An array is assigned using parentheses, and can be appended to +# with +=( … ). +declare -a flags +flags=(--foo --bar='baz') +flags+=(--greeting="Hello ${name}") +mybinary "${flags[@]}" + +# Don’t use strings for sequences. +flags='--foo --bar=baz' +flags+=' --greeting="Hello world"' # This won’t work as intended. +mybinary ${flags} + +# Command expansions return single strings, not arrays. Avoid +# unquoted expansion in array assignments because it won’t +# work correctly if the command output contains special +# characters or whitespace. + +# This expands the listing output into a string, then does special keyword +# expansion, and then whitespace splitting. Only then is it turned into a +# list of words. The ls command may also change behavior based on the user's +# active environment! +declare -a files=($(ls /directory)) + +# The get_arguments writes everything to STDOUT, but then goes through the +# same expansion process above before turning into a list of arguments. +mybinary $(get_arguments) + + +Arrays Pros +Using Arrays allows lists of things without confusing quoting semantics. Conversely, not using arrays leads to misguided attempts to nest quoting inside a string. +Arrays make it possible to safely store sequences/lists of arbitrary strings, including strings containing whitespace. + +Arrays Cons + +Using arrays can risk a script’s complexity growing. + +Arrays Decision + +Arrays should be used to safely create and pass around lists. In particular, when building a set of command arguments, use arrays to avoid confusing quoting issues. Use quoted expansion – "${array[@]}" – to access arrays. However, if more advanced data manipulation is required, shell scripting should be avoided altogether; see above. + +Pipes to While + +Use process substitution or the readarray builtin (bash4+) in preference to piping to while. Pipes create a subshell, so any variables modified within a pipeline do not propagate to the parent shell. + +The implicit subshell in a pipe to while can introduce subtle bugs that are hard to track down. + +last_line='NULL' +your_command | while read -r line; do + if [[ -n "${line}" ]]; then + last_line="${line}" + fi +done + +# This will always output 'NULL'! +echo "${last_line}" + + +Using process substitution also creates a subshell. However, it allows redirecting from a subshell to a while without putting the while (or any other command) in a subshell. + +last_line='NULL' +while read line; do + if [[ -n "${line}" ]]; then + last_line="${line}" + fi +done < <(your_command) + +# This will output the last non-empty line from your_command +echo "${last_line}" + + +Alternatively, use the readarray builtin to read the file into an array, then loop over the array’s contents. Notice that (for the same reason as above) you need to use a process substitution with readarray rather than a pipe, but with the advantage that the input generation for the loop is located before it, rather than after. + +last_line='NULL' +readarray -t lines < <(your_command) +for line in "${lines[@]}"; do + if [[ -n "${line}" ]]; then + last_line="${line}" + fi +done +echo "${last_line}" + + +Note: Be cautious using a for-loop to iterate over output, as in for var in $(...), as the output is split by whitespace, not by line. Sometimes you will know this is safe because the output can’t contain any unexpected whitespace, but where this isn’t obvious or doesn’t improve readability (such as a long command inside $(...)), a while read loop or readarray is often safer and clearer. + +Arithmetic + +Always use (( … )) or $(( … )) rather than let or $[ … ] or expr. + +Never use the $[ … ] syntax, the expr command, or the let built-in. + +< and > don’t perform numerical comparison inside [[ … ]] expressions (they perform lexicographical comparisons instead; see Testing Strings). For preference, don’t use [[ … ]] at all for numeric comparisons, use (( … )) instead. + +It is recommended to avoid using (( … )) as a standalone statement, and otherwise be wary of its expression evaluating to zero + +particularly with set -e enabled. For example, set -e; i=0; (( i++ )) will cause the shell to exit. +# Simple calculation used as text - note the use of $(( … )) within +# a string. +echo "$(( 2 + 2 )) is 4" + +# When performing arithmetic comparisons for testing +if (( a < b )); then + … +fi + +# Some calculation assigned to a variable. +(( i = 10 * j + 400 )) + +# This form is non-portable and deprecated +i=$[2 * 10] + +# Despite appearances, 'let' isn't one of the declarative keywords, +# so unquoted assignments are subject to globbing wordsplitting. +# For the sake of simplicity, avoid 'let' and use (( … )) +let i="2 + 2" + +# The expr utility is an external program and not a shell builtin. +i=$( expr 4 + 4 ) + +# Quoting can be error prone when using expr too. +i=$( expr 4 '*' 4 ) + + +Stylistic considerations aside, the shell’s built-in arithmetic is many times faster than expr. + +When using variables, the ${var} (and $var) forms are not required within $(( … )). The shell knows to look up var for you, and omitting the ${…} leads to cleaner code. This is slightly contrary to the previous rule about always using braces, so this is a recommendation only. + +# N.B.: Remember to declare your variables as integers when +# possible, and to prefer local variables over globals. +local -i hundred="$(( 10 * 10 ))" +declare -i five="$(( 10 / 2 ))" + +# Increment the variable "i" by three. +# Note that: +# - We do not write ${i} or $i. +# - We put a space after the (( and before the )). +(( i += 3 )) + +# To decrement the variable "i" by five: +(( i -= 5 )) + +# Do some complicated computations. +# Note that normal arithmetic operator precedence is observed. +hr=2 +min=5 +sec=30 +echo "$(( hr * 3600 + min * 60 + sec ))" # prints 7530 as expected + + +Aliases + +Although commonly seen in .bashrc files, aliases should be avoided in scripts. As the Bash manual notes: + +For almost every purpose, shell functions are preferred over aliases. + +Aliases are cumbersome to work with because they require carefully quoting and escaping their contents, and mistakes can be hard to notice. + +# this evaluates $RANDOM once when the alias is defined, +# so the echo'ed string will be the same on each invocation +alias random_name="echo some_prefix_${RANDOM}" + + +Functions provide a superset of alias’ functionality and should always be preferred. . + +random_name() { + echo "some_prefix_${RANDOM}" +} + +# Note that unlike aliases function's arguments are accessed via $@ +fancy_ls() { + ls -lh "$@" +} + + +Naming Conventions + +Function Names + +Lower-case, with underscores to separate words. Separate libraries with ::. Parentheses are required after the function name. The keyword function is optional, but must be used consistently throughout a project. + +If you’re writing single functions, use lowercase and separate words with underscore. If you’re writing a package, separate package names with ::. However, functions intended for interactive use may choose to avoid colons as it can confuse bash auto-completion. + +Braces must be on the same line as the function name (as with other languages at Google) and no space between the function name and the parenthesis. + +# Single function +my_func() { + … +} + +# Part of a package +mypackage::my_func() { + … +} + + +The function keyword is extraneous when “()” is present after the function name, but enhances quick identification of functions. + +Variable Names + +Same as for function names. + +Variables names for loops should be similarly named for any variable you’re looping through. + +for zone in "${zones[@]}"; do + something_with "${zone}" +done + + +Constants, Environment Variables, and readonly Variables + +Constants and anything exported to the environment should be capitalized, separated with underscores, and declared at the top of the file. + +# Constant +readonly PATH_TO_FILES='/some/path' + +# Both constant and exported to the environment +declare -xr ORACLE_SID='PROD' + + +For the sake of clarity readonly or export is recommended vs. the equivalent declare commands. You can do one after the other, like: + +# Constant +readonly PATH_TO_FILES='/some/path' +export PATH_TO_FILES + + +It’s OK to set a constant at runtime or in a conditional, but it should be made readonly immediately afterwards. + +ZIP_VERSION="$(dpkg --status zip | sed -n 's/^Version: //p')" +if [[ -z "${ZIP_VERSION}" ]]; then + ZIP_VERSION="$(pacman -Q --info zip | sed -n 's/^Version *: //p')" +fi +if [[ -z "${ZIP_VERSION}" ]]; then + handle_error_and_quit +fi +readonly ZIP_VERSION + + +Source Filenames + +Lowercase, with underscores to separate words if desired. + +This is for consistency with other code styles in Google: maketemplate or make_template but not make-template. + +Use Local Variables + +Declare function-specific variables with local. + +Ensure that local variables are only seen inside a function and its children by using local when declaring them. This avoids polluting the global namespace and inadvertently setting variables that may have significance outside the function. + +Declaration and assignment must be separate statements when the assignment value is provided by a command substitution; as the local builtin does not propagate the exit code from the command substitution. + +my_func2() { + local name="$1" + + # Separate lines for declaration and assignment: + local my_var + my_var="$(my_func)" + (( $? == 0 )) || return + + … +} + +my_func2() { + # DO NOT do this: + # $? will always be zero, as it contains the exit code of 'local', not my_func + local my_var="$(my_func)" + (( $? == 0 )) || return + + … +} + + +Function Location + +Put all functions together in the file just below constants. Don’t hide executable code between functions. Doing so makes the code difficult to follow and results in nasty surprises when debugging. + +If you’ve got functions, put them all together near the top of the file. Only includes, set statements and setting constants may be done before declaring functions. + +main + +A function called main is required for scripts long enough to contain at least one other function. + +In order to easily find the start of the program, put the main program in a function called main as the bottom-most function. This provides consistency with the rest of the code base as well as allowing you to define more variables as local (which can’t be done if the main code is not a function). The last non-comment line in the file should be a call to main: + +main "$@" + + +Obviously, for short scripts where it’s just a linear flow, main is overkill and so is not required. + +Calling Commands + +Checking Return Values + +Always check return values and give informative return values. + +For unpiped commands, use $? or check directly via an if statement to keep it simple. + +Example: + +if ! mv "${file_list[@]}" "${dest_dir}/"; then + echo "Unable to move ${file_list[*]} to ${dest_dir}" >&2 + exit 1 +fi + +# Or +mv "${file_list[@]}" "${dest_dir}/" +if (( $? != 0 )); then + echo "Unable to move ${file_list[*]} to ${dest_dir}" >&2 + exit 1 +fi + + +Bash also has the PIPESTATUS variable that allows checking of the return code from all parts of a pipe. If it’s only necessary to check success or failure of the whole pipe, then the following is acceptable: + +tar -cf - ./* | ( cd "${dir}" && tar -xf - ) +if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then + echo "Unable to tar files to ${dir}" >&2 +fi + + +However, as PIPESTATUS will be overwritten as soon as you do any other command, if you need to act differently on errors based on where it happened in the pipe, you’ll need to assign PIPESTATUS to another variable immediately after running the command (don’t forget that [ is a command and will wipe out PIPESTATUS). + +tar -cf - ./* | ( cd "${DIR}" && tar -xf - ) +return_codes=( "${PIPESTATUS[@]}" ) +if (( return_codes[0] != 0 )); then + do_something +fi +if (( return_codes[1] != 0 )); then + do_something_else +fi + + +Builtin Commands vs. External Commands + +Given the choice between invoking a shell builtin and invoking a separate process, choose the builtin. + +We prefer the use of builtins such as the Parameter Expansion functionality provided by bash as it’s more efficient, robust, and portable (especially when compared to things like sed). See also the =~ operator. + +Examples: + +# Prefer this: +addition="$(( X + Y ))" +substitution="${string/#foo/bar}" +if [[ "${string}" =~ foo:(\d+) ]]; then + extraction="${BASH_REMATCH[1]}" +fi + +# Instead of this: +addition="$(expr "${X}" + "${Y}")" +substitution="$(echo "${string}" | sed -e 's/^foo/bar/')" +extraction="$(echo "${string}" | sed -e 's/foo:\([0-9]\)/\1/')" + + +When in Doubt: Be Consistent + +Using one style consistently through our codebase lets us focus on other (more important) issues. Consistency also allows for automation. In many cases, rules that are attributed to “Be Consistent” boil down to “Just pick one and stop worrying about it”; the potential value of allowing flexibility on these points is outweighed by the cost of having people argue over them. + +However, there are limits to consistency. It is a good tie breaker when there is no clear technical argument, nor a long-term direction. Consistency should not generally be used as a justification to do things in an old style without considering the benefits of the new style, or the tendency of the codebase to converge on newer styles over time. diff --git a/ai/skills/tdd/SKILL.md b/ai/skills/tdd/SKILL.md new file mode 100644 index 0000000..6c2ac12 --- /dev/null +++ b/ai/skills/tdd/SKILL.md @@ -0,0 +1,109 @@ +--- +name: tdd +description: Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development. +--- + +# Test-Driven Development + +## Philosophy + +**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. + +**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure. + +**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior. + +See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines. + +## Anti-Pattern: Horizontal Slices + +**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code." + +This produces **crap tests**: + +- Tests written in bulk test _imagined_ behavior, not _actual_ behavior +- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior +- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine +- You outrun your headlights, committing to test structure before understanding the implementation + +**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it. + +``` +WRONG (horizontal): + RED: test1, test2, test3, test4, test5 + GREEN: impl1, impl2, impl3, impl4, impl5 + +RIGHT (vertical): + RED→GREEN: test1→impl1 + RED→GREEN: test2→impl2 + RED→GREEN: test3→impl3 + ... +``` + +## Workflow + +### 1. Planning + +When exploring the codebase, use the project's domain glossary so that test names and interface vocabulary match the project's language, and respect ADRs in the area you're touching. + +Before writing any code: + +- [ ] Confirm with user what interface changes are needed +- [ ] Confirm with user which behaviors to test (prioritize) +- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation) +- [ ] Design interfaces for [testability](interface-design.md) +- [ ] List the behaviors to test (not implementation steps) +- [ ] Get user approval on the plan + +Ask: "What should the public interface look like? Which behaviors are most important to test?" + +**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case. + +### 2. Tracer Bullet + +Write ONE test that confirms ONE thing about the system: + +``` +RED: Write test for first behavior → test fails +GREEN: Write minimal code to pass → test passes +``` + +This is your tracer bullet - proves the path works end-to-end. + +### 3. Incremental Loop + +For each remaining behavior: + +``` +RED: Write next test → fails +GREEN: Minimal code to pass → passes +``` + +Rules: + +- One test at a time +- Only enough code to pass current test +- Don't anticipate future tests +- Keep tests focused on observable behavior + +### 4. Refactor + +After all tests pass, look for [refactor candidates](refactoring.md): + +- [ ] Extract duplication +- [ ] Deepen modules (move complexity behind simple interfaces) +- [ ] Apply SOLID principles where natural +- [ ] Consider what new code reveals about existing code +- [ ] Run tests after each refactor step + +**Never refactor while RED.** Get to GREEN first. + +## Checklist Per Cycle + +``` +[ ] Test describes behavior, not implementation +[ ] Test uses public interface only +[ ] Test would survive internal refactor +[ ] Code is minimal for this test +[ ] No speculative features added +``` \ No newline at end of file diff --git a/ai/skills/tdd/deep-modules.md b/ai/skills/tdd/deep-modules.md new file mode 100644 index 0000000..13a1b91 --- /dev/null +++ b/ai/skills/tdd/deep-modules.md @@ -0,0 +1,33 @@ +# Deep Modules + +From "A Philosophy of Software Design": + +**Deep module** = small interface + lots of implementation + +``` +┌─────────────────────┐ +│ Small Interface │ ← Few methods, simple params +├─────────────────────┤ +│ │ +│ │ +│ Deep Implementation│ ← Complex logic hidden +│ │ +│ │ +└─────────────────────┘ +``` + +**Shallow module** = large interface + little implementation (avoid) + +``` +┌─────────────────────────────────┐ +│ Large Interface │ ← Many methods, complex params +├─────────────────────────────────┤ +│ Thin Implementation │ ← Just passes through +└─────────────────────────────────┘ +``` + +When designing interfaces, ask: + +- Can I reduce the number of methods? +- Can I simplify the parameters? +- Can I hide more complexity inside? \ No newline at end of file diff --git a/ai/skills/tdd/interface-design.md b/ai/skills/tdd/interface-design.md new file mode 100644 index 0000000..5bbed3e --- /dev/null +++ b/ai/skills/tdd/interface-design.md @@ -0,0 +1,31 @@ +# Interface Design for Testability + +Good interfaces make testing natural: + +1. **Accept dependencies, don't create them** + + ```typescript + // Testable + function processOrder(order, paymentGateway) {} + + // Hard to test + function processOrder(order) { + const gateway = new StripeGateway(); + } + ``` + +2. **Return results, don't produce side effects** + + ```typescript + // Testable + function calculateDiscount(cart): Discount {} + + // Hard to test + function applyDiscount(cart): void { + cart.total -= discount; + } + ``` + +3. **Small surface area** + - Fewer methods = fewer tests needed + - Fewer params = simpler test setup \ No newline at end of file diff --git a/ai/skills/tdd/mocking.md b/ai/skills/tdd/mocking.md new file mode 100644 index 0000000..a280617 --- /dev/null +++ b/ai/skills/tdd/mocking.md @@ -0,0 +1,59 @@ +# When to Mock + +Mock at **system boundaries** only: + +- External APIs (payment, email, etc.) +- Databases (sometimes - prefer test DB) +- Time/randomness +- File system (sometimes) + +Don't mock: + +- Your own classes/modules +- Internal collaborators +- Anything you control + +## Designing for Mockability + +At system boundaries, design interfaces that are easy to mock: + +**1. Use dependency injection** + +Pass external dependencies in rather than creating them internally: + +```typescript +// Easy to mock +function processPayment(order, paymentClient) { + return paymentClient.charge(order.total); +} + +// Hard to mock +function processPayment(order) { + const client = new StripeClient(process.env.STRIPE_KEY); + return client.charge(order.total); +} +``` + +**2. Prefer SDK-style interfaces over generic fetchers** + +Create specific functions for each external operation instead of one generic function with conditional logic: + +```typescript +// GOOD: Each function is independently mockable +const api = { + getUser: (id) => fetch(`/users/${id}`), + getOrders: (userId) => fetch(`/users/${userId}/orders`), + createOrder: (data) => fetch('/orders', { method: 'POST', body: data }), +}; + +// BAD: Mocking requires conditional logic inside the mock +const api = { + fetch: (endpoint, options) => fetch(endpoint, options), +}; +``` + +The SDK approach means: +- Each mock returns one specific shape +- No conditional logic in test setup +- Easier to see which endpoints a test exercises +- Type safety per endpoint \ No newline at end of file diff --git a/ai/skills/tdd/refactoring.md b/ai/skills/tdd/refactoring.md new file mode 100644 index 0000000..e184a15 --- /dev/null +++ b/ai/skills/tdd/refactoring.md @@ -0,0 +1,10 @@ +# Refactor Candidates + +After TDD cycle, look for: + +- **Duplication** → Extract function/class +- **Long methods** → Break into private helpers (keep tests on public interface) +- **Shallow modules** → Combine or deepen +- **Feature envy** → Move logic to where data lives +- **Primitive obsession** → Introduce value objects +- **Existing code** the new code reveals as problematic \ No newline at end of file diff --git a/ai/skills/tdd/tests.md b/ai/skills/tdd/tests.md new file mode 100644 index 0000000..65e2e71 --- /dev/null +++ b/ai/skills/tdd/tests.md @@ -0,0 +1,61 @@ +# Good and Bad Tests + +## Good Tests + +**Integration-style**: Test through real interfaces, not mocks of internal parts. + +```typescript +// GOOD: Tests observable behavior +test("user can checkout with valid cart", async () => { + const cart = createCart(); + cart.add(product); + const result = await checkout(cart, paymentMethod); + expect(result.status).toBe("confirmed"); +}); +``` + +Characteristics: + +- Tests behavior users/callers care about +- Uses public API only +- Survives internal refactors +- Describes WHAT, not HOW +- One logical assertion per test + +## Bad Tests + +**Implementation-detail tests**: Coupled to internal structure. + +```typescript +// BAD: Tests implementation details +test("checkout calls paymentService.process", async () => { + const mockPayment = jest.mock(paymentService); + await checkout(cart, payment); + expect(mockPayment.process).toHaveBeenCalledWith(cart.total); +}); +``` + +Red flags: + +- Mocking internal collaborators +- Testing private methods +- Asserting on call counts/order +- Test breaks when refactoring without behavior change +- Test name describes HOW not WHAT +- Verifying through external means instead of interface + +```typescript +// BAD: Bypasses interface to verify +test("createUser saves to database", async () => { + await createUser({ name: "Alice" }); + const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]); + expect(row).toBeDefined(); +}); + +// GOOD: Verifies through interface +test("createUser makes user retrievable", async () => { + const user = await createUser({ name: "Alice" }); + const retrieved = await getUser(user.id); + expect(retrieved.name).toBe("Alice"); +}); +``` \ No newline at end of file diff --git a/ai/skills/to-prd/SKILL.md b/ai/skills/to-prd/SKILL.md new file mode 100644 index 0000000..359c2c6 --- /dev/null +++ b/ai/skills/to-prd/SKILL.md @@ -0,0 +1,76 @@ +--- +name: to-prd +description: Turn the current conversation context into a PRD and publish it to the project issue tracker. Use when user wants to create a PRD from the current context. +--- + +This skill takes the current conversation context and codebase understanding and produces a PRD. Do NOT interview the user — just synthesize what you already know. + +The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not. + +## Process + +1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the PRD, and respect any ADRs in the area you're touching. + +2. Sketch out the major modules you will need to build or modify to complete the implementation. Actively look for opportunities to extract deep modules that can be tested in isolation. + +A deep module (as opposed to a shallow module) is one which encapsulates a lot of functionality in a simple, testable interface which rarely changes. + +Check with the user that these modules match their expectations. Check with the user which modules they want tests written for. + +3. Write the PRD using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage. + +<prd-template> + +## Problem Statement + +The problem that the user is facing, from the user's perspective. + +## Solution + +The solution to the problem, from the user's perspective. + +## User Stories + +A LONG, numbered list of user stories. Each user story should be in the format of: + +1. As an <actor>, I want a <feature>, so that <benefit> + +<user-story-example> +1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending +</user-story-example> + +This list of user stories should be extremely extensive and cover all aspects of the feature. + +## Implementation Decisions + +A list of implementation decisions that were made. This can include: + +- The modules that will be built/modified +- The interfaces of those modules that will be modified +- Technical clarifications from the developer +- Architectural decisions +- Schema changes +- API contracts +- Specific interactions + +Do NOT include specific file paths or code snippets. They may end up being outdated very quickly. + +Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits. + +## Testing Decisions + +A list of testing decisions that were made. Include: + +- A description of what makes a good test (only test external behavior, not implementation details) +- Which modules will be tested +- Prior art for the tests (i.e. similar types of tests in the codebase) + +## Out of Scope + +A description of the things that are out of scope for this PRD. + +## Further Notes + +Any further notes about the feature. + +</prd-template> \ No newline at end of file diff --git a/ai/skills/web-to-markdown/SKILL.md b/ai/skills/web-to-markdown/SKILL.md new file mode 100644 index 0000000..9f089fc --- /dev/null +++ b/ai/skills/web-to-markdown/SKILL.md @@ -0,0 +1,34 @@ +--- +name: web-to-markdown +description: "Use when the user asks to grab, download, or copy an article from a website (e.g. 'grab me a copy of <url>' or 'convert <url> to markdown'). This skill uses the web-to-markdown utility to convert articles to markdown and download inline images." +--- + +# Web-to-Markdown Skill + +This skill allows the agent to download an HTML page, extract its main article content, convert it to Markdown, and download all inline images using the local `web-to-markdown` utility. + +## Prerequisites +- The utility is located at `/srv/dev/web-to-markdown/web-to-markdown`. +- Always check that this executable exists before proceeding. + +## Workflow + +When the user asks to download or grab a copy of a website article, follow these steps: + +1. **Ask for configuration:** + Before running the script, ask the user: + - **Destination:** Where should the article be saved? (Suggest the current directory or a logical default like `docs/` or `notes/`). + - **Title:** What should the folder/article title be? (Suggest a simplified version of the URL slug or ask if they want the tool to extract the default title by omitting the title flag). + +2. **Execute the Utility:** + Once you have the configuration, use the `run_shell_command` tool to execute the `web-to-markdown` utility. + + **Command Syntax:** + ` /srv/dev/web-to-markdown/web-to-markdown -out "<destination_path>" -title "<custom_title>" "<url>" ` + *Note: Omit `-title "<custom_title>"` if the user wants the tool to use the default parsed title.* + +3. **Verify and Confirm:** + - Review the output of the shell command to ensure it was successful. + - List the target directory to verify the `index.md` and any downloaded images exist. + - Inform the user of the successful download and the exact path where the new markdown file is located. + diff --git a/ai/skills/workflow_management/SKILL.md b/ai/skills/workflow_management/SKILL.md new file mode 100644 index 0000000..5920b0d --- /dev/null +++ b/ai/skills/workflow_management/SKILL.md @@ -0,0 +1,25 @@ +--- +name: workflow-management +description: Specialized skill for creating, updating, and validating workflow documents in the Gemini Workflow Framework. +--- + +# Workflow Management Skill + +This skill allows Gemini to efficiently manage the lifecycle of project workflows. + +## Functions + +1. **Workflow Creation**: Propose new workflows using the standardized format (Objective, Action Items, Verification). +2. **Step Mapping**: Automatically map high-level project goals to discrete, verifiable workflow steps. +3. **Public Audit**: For each step, identify and link to the relevant public documentation (e.g., GitHub, MDN, public API docs). +4. **Consistency Check**: Ensure that all action items align with the "Public Constraint Protocol" in `GEMINI.md`. + +## Standard Workflow Format + +### Workflow: [Name] + +#### Step [X]: [Objective Summary] +- **Goal**: [Detailed goal] +- **Action Items**: + - [ ] [Specific task] +- **Verification**: [Public-domain method to verify success] diff --git a/ai/skills/write-a-skill/SKILL.md b/ai/skills/write-a-skill/SKILL.md new file mode 100644 index 0000000..904dfb1 --- /dev/null +++ b/ai/skills/write-a-skill/SKILL.md @@ -0,0 +1,117 @@ +--- +name: write-a-skill +description: Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill. +--- + +# Writing Skills + +## Process + +1. **Gather requirements** - ask user about: + - What task/domain does the skill cover? + - What specific use cases should it handle? + - Does it need executable scripts or just instructions? + - Any reference materials to include? + +2. **Draft the skill** - create: + - SKILL.md with concise instructions + - Additional reference files if content exceeds 500 lines + - Utility scripts if deterministic operations needed + +3. **Review with user** - present draft and ask: + - Does this cover your use cases? + - Anything missing or unclear? + - Should any section be more/less detailed? + +## Skill Structure + +``` +skill-name/ +├── SKILL.md # Main instructions (required) +├── REFERENCE.md # Detailed docs (if needed) +├── EXAMPLES.md # Usage examples (if needed) +└── scripts/ # Utility scripts (if needed) + └── helper.js +``` + +## SKILL.md Template + +```md +--- +name: skill-name +description: Brief description of capability. Use when [specific triggers]. +--- + +# Skill Name + +## Quick start + +[Minimal working example] + +## Workflows + +[Step-by-step processes with checklists for complex tasks] + +## Advanced features + +[Link to separate files: See [REFERENCE.md](REFERENCE.md)] +``` + +## Description Requirements + +The description is **the only thing your agent sees** when deciding which skill to load. It's surfaced in the system prompt alongside all other installed skills. Your agent reads these descriptions and picks the relevant skill based on the user's request. + +**Goal**: Give your agent just enough info to know: + +1. What capability this skill provides +2. When/why to trigger it (specific keywords, contexts, file types) + +**Format**: + +- Max 1024 chars +- Write in third person +- First sentence: what it does +- Second sentence: "Use when [specific triggers]" + +**Good example**: + +``` +Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when user mentions PDFs, forms, or document extraction. +``` + +**Bad example**: + +``` +Helps with documents. +``` + +The bad example gives your agent no way to distinguish this from other document skills. + +## When to Add Scripts + +Add utility scripts when: + +- Operation is deterministic (validation, formatting) +- Same code would be generated repeatedly +- Errors need explicit handling + +Scripts save tokens and improve reliability vs generated code. + +## When to Split Files + +Split into separate files when: + +- SKILL.md exceeds 100 lines +- Content has distinct domains (finance vs sales schemas) +- Advanced features are rarely needed + +## Review Checklist + +After drafting, verify: + +- [ ] Description includes triggers ("Use when...") +- [ ] SKILL.md under 100 lines +- [ ] No time-sensitive info +- [ ] Consistent terminology +- [ ] Concrete examples included +- [ ] References one level deep \ No newline at end of file