Skip to content

Added ADR for architectural standards#2358

Open
mrrajan wants to merge 4 commits into
guacsec:mainfrom
mrrajan:TC-4289
Open

Added ADR for architectural standards#2358
mrrajan wants to merge 4 commits into
guacsec:mainfrom
mrrajan:TC-4289

Conversation

@mrrajan
Copy link
Copy Markdown
Contributor

@mrrajan mrrajan commented May 14, 2026

Summary

  • Add ADR-00018 proposing a CONVENTIONS.md file and documenting pattern standards for the Trustify project
  • Catalogs recurring anti-patterns found across the codebase (N+1 queries, unbounded collections, sequential independent queries, missing database indexes, uncontrolled pagination, redundant deserialization) with specific file locations and severity ratings
  • Presents convention options for each anti-pattern category for the team to evaluate during PR review

Rendered version https://github.com/mrrajan/trustify/blob/TC-4289/docs/adrs/00018-conventions-file.md

Summary by Sourcery

Introduce an ADR proposing a centralized CONVENTIONS.md file and cataloging key performance and coding anti‑patterns in the Trustify codebase, along with candidate standards for addressing them.

Documentation:

  • Add ADR-00017 documenting the rationale, scope, and maintenance process for a repository-wide CONVENTIONS.md conventions file used by contributors and AI tools.
  • Document a detailed analysis of recurring performance and coding anti-patterns across the codebase, including their locations and proposed convention options for remediation.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 14, 2026

Reviewer's Guide

Adds ADR-00017 describing the introduction, purpose, and lifecycle of a repository-level CONVENTIONS.md file and documenting a detailed catalog of existing performance and coding anti-patterns in the Trustify codebase, along with convention options the team should select during review.

File-Level Changes

Change Details Files
Introduce ADR-00017 defining a repository-level CONVENTIONS.md file as the canonical source of coding and architectural conventions, optimized for AI-assisted development workflows.
  • Specify scope, content structure, and maintenance process for CONVENTIONS.md at the repo root
  • Clarify that conventions are prescriptive, derived from existing patterns, and kept minimal and actionable
  • Describe how AI tools (e.g., Claude Code) will consume CONVENTIONS.md and how it relates to any CLAUDE.md configuration
  • Define governance for updating conventions via PRs and linking significant changes to ADRs
docs/adrs/00017-conventions-file.md
Document a comprehensive analysis of recurring performance anti-patterns in the existing codebase, with concrete examples and convention options to adopt.
  • Catalog N+1 queries, unbounded queries, in-memory filtering, app-side counting, sequential DB calls, missing bulk operations, unbounded recursion, extra round-trips vs JOINs, validation-plus-follow-on queries, and missing indexes
  • For each category, explain what it is, why it matters, where it occurs (file/line-level references), and propose option sets (A/B/C) for conventions
  • Summarize performance anti-patterns in a table to guide reviewers in selecting preferred options that will later be codified in CONVENTIONS.md
docs/adrs/00017-conventions-file.md
Document a comprehensive analysis of recurring coding anti-patterns in the existing codebase, with concrete examples and convention options to adopt.
  • Catalog swallowed errors, stringly-typed APIs, code duplication, tight module coupling, oversized functions, inconsistent tracing, missing public docs, mixed logging frameworks, magic numbers, and raw SQL patterns that defeat parameterization
  • For each category, provide rationale, specific occurrences, and option sets (A/B/C) indicating recommended conventions versus laxer alternatives
  • Summarize coding anti-patterns in a final table and describe how selected options should be propagated into CONVENTIONS.md in a follow-up change
docs/adrs/00017-conventions-file.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@mrrajan mrrajan marked this pull request as ready for review May 14, 2026 12:22
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The ADR is very long and mixes the high-level decision (introducing CONVENTIONS.md) with a detailed audit of anti-patterns; consider splitting the anti-pattern catalog into a separate document (or appendix) to keep the ADR focused on the architectural decision and process.
  • Many examples reference approximate line numbers and specific file locations that will quickly become stale; instead of line-based pointers, consider linking to symbols or providing higher-level file/section references that will age more gracefully.
  • You define option sets (A/B/C) for many categories but the process for capturing the final choice in CONVENTIONS.md is only described informally; consider adding a concise, explicit section on how and where the selected options are recorded and how future ADRs should evolve or override them.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The ADR is very long and mixes the high-level decision (introducing CONVENTIONS.md) with a detailed audit of anti-patterns; consider splitting the anti-pattern catalog into a separate document (or appendix) to keep the ADR focused on the architectural decision and process.
- Many examples reference approximate line numbers and specific file locations that will quickly become stale; instead of line-based pointers, consider linking to symbols or providing higher-level file/section references that will age more gracefully.
- You define option sets (A/B/C) for many categories but the process for capturing the final choice in CONVENTIONS.md is only described informally; consider adding a concise, explicit section on how and where the selected options are recorded and how future ADRs should evolve or override them.

## Individual Comments

### Comment 1
<location path="docs/adrs/00017-conventions-file.md" line_range="107" />
<code_context>
+
+## Anti-Pattern Analysis
+
+Analysis of the trustify codebase has identified recurring anti-patterns across
+multiple modules. This section catalogs each category, lists the occurrences found, and
+presents convention options for the team to evaluate. The selected conventions will be
</code_context>
<issue_to_address>
**nitpick (typo):** Capitalize the project name "Trustify" for consistency.

This line uses lowercase "trustify" while earlier references use "Trustify"; update this occurrence to match the proper noun capitalization.

```suggestion
Analysis of the Trustify codebase has identified recurring anti-patterns across
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/adrs/00017-conventions-file.md Outdated
@mrrajan mrrajan force-pushed the TC-4289 branch 2 times, most recently from 156ce43 to 79bfc3b Compare May 14, 2026 12:32
@PhilipCattanach
Copy link
Copy Markdown

Thanks @mrrajan Very interesting reading
I would suggest choosing the appropriate convention for some of the performance issue will probably need tests at scale. I'm concerned that some of the potential solutions might make things worse at scale.

I'd like us to come up with a way of prioritizing our next steps. As I would like to think there is some non contentious conventions we can adopt that will address some of these things.

And we might need some Spikes to evaluate the best convention for some of the issues highlighted.

Copy link
Copy Markdown
Contributor

@mrizzi mrizzi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A first review of the methodology before getting into the anti-patterns analysis with the maintainers.

### Content Principles

1. **Prescriptive, not descriptive**: each convention states what to do and what to avoid, with concrete code examples
2. **Derived from existing code**: conventions are extracted from established patterns in the codebase, not invented
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the initial version but not once stable because, once fully adopted, it should be other way around, i.e. code should be derived from existing conventions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question is, how do we deal with current code that doesn't align to those newly defined patterns?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 👍

Sending random and sometimes partial PRs without JIRAs?

side note: Yesterday I asked AI to read the instrumentation section and to produce a checklist for me, not changed the code yet as some parts are subjective (example -> the size of returned struct data) + still need to validate if all the checklist is 100% correct. Imagining all this checklist is correct, all the instrumentation-related-code from all files should be updated via a single PR? Should this initiative be separated by our current workspace-crates to avoid git conflicts and long PR reviews?

Copy link
Copy Markdown
Contributor

@mrizzi mrizzi May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question is, how do we deal with current code that doesn't align to those newly defined patterns?

It will mean having tech debt initiatives to enforce consistency in current code not yet aligned with expected patterns and code conventions.

Comment thread docs/adrs/00018-conventions-file.md Outdated

### Trade-offs

- **Maintenance burden**: the file must be kept in sync with evolving practices — stale conventions are worse than no conventions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this exists because, as described in the above Maintenance Process, when a convention has to change, then an ADR arrives. Once merged, the impacted code has to refactored to be aligned with the new code convention. I don't see how "stale conventions" could happen other than implementing code not aligned with the conventions, which should be considered a mistake to prevent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my understanding, feedback from reviews might turn in PR changing the conventions file? If that's the case, then this scenario may happen. Unless of course, a change in the conventions file is accompanied by reworking the code base.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes from PRs are meant to be additive to improve code conventions for identified lacks. Changing them to "adhere" to the code in a PR should be blocked otherwise code will drift from conventions.

- **Occurrences found** — specific locations in the codebase
- **Convention options** — choices for the team to decide on (to be resolved via PR review)

> **How to use this section**: During PR review, maintainers should mark their preferred
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcrossley3 @ctron @ruromero @queria please review this

@ctron
Copy link
Copy Markdown
Contributor

ctron commented May 15, 2026

I'm not sure an ADR is the right approach to this. We already have the conventions file, and this ADR would need to be copied over to that file in a second step.

Why no simply make the modifications to the conventions file and discuss this on a PR? Because what would we review on that second PR (moving content over to the conventions file)? Feels like doppelmoppel.

Comment thread docs/adrs/00018-conventions-file.md Outdated

## Context

Trustify uses AI-assisted development workflows (Claude Code, Copilot, and similar tools). These tools perform best when they have access to explicit, machine-readable project conventions — coding patterns, naming rules, error-handling idioms, testing practices, and architectural norms.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we do have some documentation (for less then we should have) for humans. I believe that this file should be usable by humans as well. Replacing the existing documents. Which means, that this file should be structured and written to serve both.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! Rephrased to,

Trustify needs a single, explicit reference for coding patterns, naming rules, error-handling idioms, testing practices, and architectural norms. Contributors and reviewers use it during implementation and review; the same clarity also helps AI-assisted workflows (Claude Code, Copilot, and similar tools) when those tools load project context.

Comment thread docs/adrs/00018-conventions-file.md Outdated
| **Entity Model Patterns** | ORM model conventions | `DeriveEntityModel`, relations, `Linked` structs |
| **Migration Patterns** | Database migration conventions | Idempotency guards, naming, raw SQL loading |
| **Rust Idioms** | Preferred Rust patterns | Type inference, iterator ownership, `.zip()`, capacity |
| **SeaORM Query Patterns** | ORM query conventions | `.is_in()`, chunking, `IntoIterator` parameters |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IntoIterator wouldn't be a SeaORM thing, but a Rust thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - Removed

Comment thread docs/adrs/00018-conventions-file.md Outdated
| **Rust Idioms** | Preferred Rust patterns | Type inference, iterator ownership, `.zip()`, capacity |
| **SeaORM Query Patterns** | ORM query conventions | `.is_in()`, chunking, `IntoIterator` parameters |
| **Observability** | Tracing and instrumentation | `#[instrument]` usage, span conventions, error levels |
| **Shared Table Patterns** | Concurrent insert handling | Nested transaction duplicate-key and `.on_conflict` patterns |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the scope of this section. I'd prefer to have some "database" section maybe. Defining how database resources/entities are created, column types, tables, enums, indexes, ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! We should have a dedicated database section.

Comment thread docs/adrs/00018-conventions-file.md Outdated
1. **Prescriptive, not descriptive**: each convention states what to do and what to avoid, with concrete code examples
2. **Derived from existing code**: conventions are extracted from established patterns in the codebase, not invented
3. **Minimal and actionable**: each entry should be short enough that a contributor (or AI tool) can apply it without reading surrounding prose
4. **Reference implementations**: point to specific files in the codebase as canonical examples
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that may be tricky. As those code locations will change over time. And that would mean updating the conventions file, or dead links.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! We should have just the examples not the references to the specific files. Rephrased to share canonical examples on the conventions file.

- Claude Code automatically loads `CONVENTIONS.md` from the repository root as part of its project context
- The file uses markdown with code blocks, making it parseable by any LLM
- Conventions are structured as clear rules with examples, optimizing for AI instruction-following
- When a `CLAUDE.md` file is present (for tool-specific configuration), `CONVENTIONS.md` complements it — `CONVENTIONS.md` focuses on language and framework patterns that apply regardless of the tool
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how that aligns with review tools? like sourcery.ai?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcery.ai applies generic best practices and CONVENTIONS.md captures project specific ones. So, project specific practices might called out by the review tools. We have to explore how to align review tools to use CONVENTIONS.md file as reference.

Comment thread docs/adrs/00018-conventions-file.md Outdated
Comment thread docs/adrs/00018-conventions-file.md
## Anti-Pattern Analysis

Analysis of the Trustify codebase has identified recurring anti-patterns across
multiple modules. This section catalogs each category, lists the occurrences found, and
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the same problem as before. If we "point" to those locations, and those locations will be gone over time (which should be the case with anti-patterns) we have a document of dead links.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! The references should use function/ symbol names or example snippets in the CONVENTIONS.md file

Comment thread docs/adrs/00018-conventions-file.md Outdated

Each category below follows this structure:
- **What it is** — description of the anti-pattern
- **Why it matters** — performance impact
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume "performance impact" is an example. But in general, "why" can be all kind of reasons.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! rephrased to performance impact, maintainability, consistency

Comment thread docs/adrs/00018-conventions-file.md Outdated
Each category below follows this structure:
- **What it is** — description of the anti-pattern
- **Why it matters** — performance impact
- **Occurrences found** — specific locations in the codebase
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment about links above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! This should be references not specific locations in codebase.

**What it is**: Database queries executed inside loops — fetching related data one entity
at a time instead of loading all related data in a single batch query.

**Why it matters**: For a list of N entities, this generates N additional queries instead of
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the "pro" side, it may reduce code complexity. In some cases dramatically. And when it is known that "N" will always be a small number, and the operation itself is fast. Then keeping the N+1 pattern would be a good thing IMHO. It depends on the case.

1 batch query. On API read paths serving collections, this scales linearly with result size
and dominates response latency.

**Occurrences found (5)**:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a lot more in, especially in the "analysis" module.


**Occurrences found (5)**:

| # | File | Line(s) | Description | Severity |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is "severity" evaluated here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity is based on,

  • Whether the N is bounded or not
  • Code path is API or internal


**Convention options**:

- **Option A — Batch with JOINs/IN clauses**: All collection data access MUST use batch
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- **Option C — Keep as-is**: No convention. Performance is addressed case-by-case when
bottlenecks are observed. N+1 patterns are acceptable if the loop iteration count is
expected to be small.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say "Option D": prefer option A, but fall back to "N+1" if there are good reasons.

"C" doesn't seem to cover this, as it says "no convention". I think the should try, and try hard to remediate N+1 patterns. But not at all cost.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Added option D

- **Option D — Prefer batch, allow exceptions**: Default to Option A — batch loading on
  collection paths and remediate existing N+1 patterns as tech debt. Per-entity DB calls
  inside loops are permitted only when batching is impractical or costly. 


| # | File | Line(s) | Description |
|---|------|---------|-------------|
| 1 | `modules/importer/src/service.rs` | ~133 | `importer::Entity::find().all()` fetches all importers, sorts in memory |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which I don't see as a problem, as the number of importers is considered small (below 50). Sure it may be an improvement. But in some cases it's not. Having pagination on the HTTP API would be beneficial.

**Why it matters**: As data grows, unbounded queries consume increasing memory and network
bandwidth. A single unbounded query on a large table can cause OOM or timeout.

**Occurrences found (3)**:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, there is more. Especially in the analysis module.


### AP-2: Unbounded Queries

**What it is**: Queries that fetch all rows from a table or relation without applying
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases we load all data, and then keep in memory to process it. Especially as mentioned above to do things like batch loading, pre-loading, ...

In such cases it doesn't seem to make sense to use limits, as we load and store the data in-memory anyway. In fact, it would be counter productive (even batch loading). Batch loading only makes sense to limit the number of postgres parameters. Having to page through data would mean to execute the query multiple times. Compared to one single query.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! lets limit only for the public API endpoints

must use `.limit()` or chunked iteration. Exceptions only for tables with a known small
upper bound (e.g., importers, which are admin-configured).

- **Option B — Limit only on public API endpoints**: Public API list endpoints must be
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option B 👍

Comment thread docs/adrs/00018-conventions-file.md Outdated

| # | File | Line(s) | Description |
|---|------|---------|-------------|
| 1 | `modules/fundamental/src/vulnerability/model/details/mod.rs` | ~54-68 | All scores fetched via `.all(tx)`, then filtered with `.filter(\|s\| s.advisory_id == ...)` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that's a false positive. As the code does re-use that data. But eliminates a second query by re-using that data and manually filtering out information for the first use case of the data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! It is a false positive. I will remove this example section - But as a convention for this issue, shall we suggest option B?


| # | File | Line(s) | Pattern | Severity |
|---|------|---------|---------|----------|
| 1 | `modules/importer/src/model/mod.rs` | ~331 | `serde_json::from_value(report).ok()` — importer run report deserialization silently dropped | High |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, this provides additional diagnostic information, if possible. If the serialization of that fails, that would just not be possible. Logging would be fine, but throwing an error not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! It is additional noise

| # | File | Line(s) | Pattern | Severity |
|---|------|---------|---------|----------|
| 1 | `modules/importer/src/model/mod.rs` | ~331 | `serde_json::from_value(report).ok()` — importer run report deserialization silently dropped | High |
| 2 | `modules/importer/src/server/progress.rs` | ~48, 77 | `let _ = self.service.set_progress_message(...)` — DB write failures for progress silently discarded | High |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it would actually be beneficial to add this to the instrumentation. Rather than an explicit log call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree!

| 1 | `modules/importer/src/model/mod.rs` | ~331 | `serde_json::from_value(report).ok()` — importer run report deserialization silently dropped | High |
| 2 | `modules/importer/src/server/progress.rs` | ~48, 77 | `let _ = self.service.set_progress_message(...)` — DB write failures for progress silently discarded | High |
| 3 | `modules/importer/src/server/mod.rs` | ~186 | `serde_json::to_value(report).ok()` — importer run report serialization silently dropped on `update_finish`; report data lost from database | High |
| 4 | `modules/importer/src/model/mod.rs` | ~65 | `OffsetDateTime::from_unix_timestamp_nanos(t).ok()` — heartbeat timestamp errors silently dropped | Medium |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it might actually be beneficial to remove the root cause of this. Using a proper timestamp in the database, if possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! we should fix the root cause instead!

| 2 | `modules/importer/src/server/progress.rs` | ~48, 77 | `let _ = self.service.set_progress_message(...)` — DB write failures for progress silently discarded | High |
| 3 | `modules/importer/src/server/mod.rs` | ~186 | `serde_json::to_value(report).ok()` — importer run report serialization silently dropped on `update_finish`; report data lost from database | High |
| 4 | `modules/importer/src/model/mod.rs` | ~65 | `OffsetDateTime::from_unix_timestamp_nanos(t).ok()` — heartbeat timestamp errors silently dropped | Medium |
| 5 | `modules/ingestor/src/service/advisory/csaf/loader.rs` | ~44, 48 | `published`/`modified` timestamp conversions silently become `None` | Medium |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it would be beneficial to report this back to the user, as we do have a mechanism for reporting such things as "warning" during the upload. This would also end up in the report. Logging this, or failing, wouldn't help.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the other CSAF related ones.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!


- **Option C — Keep as-is**: No convention. Developers judge whether an error is worth
logging. `.ok()` is acceptable for non-critical conversions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option D:

  • if we have instrumentation wrapping it, that's ok, no need for extra processing
  • if we have a way to report this case back to the user, leverage this, but don't extra log it
  • if that case prevents us from processing the data, and the data is essential, propagate the error
  • if running into an error is an acceptable and expected case, that's fine, do nothing
  • if we can ignore the error, but have no other way to handle it, log it, but try to be compact doing it, e.g. like .inspect_err() rather than a full blown match.


**Convention options**:

- **Option A — Enums for fixed value sets**: Any value drawn from a fixed set (statuses,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option A


**Convention options**:

- **Option A — Extract shared logic into generics or traits**: When two or more modules
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option A

composition. However, the `fundamental` layer MUST NOT depend on `ingestor` — shared
types like `Deprecation` must be moved to `common/`.

- **Option C — Keep as-is**: No convention. Cross-module imports are acceptable within
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option C

phase into a private helper method with a descriptive name. The parent function becomes
a coordinator that calls the helpers in sequence.

- **Option B — Maximum cyclomatic complexity**: No hard line limit, but functions with
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option B. And I'd say it is ok to use inner strucs, fns, ... for doing this.


- **Option C — Keep as-is**: No convention. Instrumentation is added when needed for
debugging. Partial coverage is acceptable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option D: Option A, but with a twist. I think this is already well described in the tracing/logging markdown file. In a nutshell, everything that is calling out to external things (DB, storage, ... ) should be wrapped in instrumentation. Everything that does not, as long as it is considered trivial, must not be wrapped. Even if things cannot be wrapped (multiple sections of loading where loading functions don't have instrumentation) use .instrument or other constructs amending this. But don't do it twice (inner vs outer). Not limited to pub, but based on logic blocks and the fact that external factors are in play.


**Convention options**:

- **Option A — Document all public items**: Every public struct, enum, trait, function,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option A.

In addition, also add non-verbose comments inside functions.

Comment thread docs/adrs/00018-conventions-file.md Outdated
and `tracing::instrument`/`tracing::Instrument` attributes, creating inconsistent
observability output.

**Why it matters**: `tracing` and `log` have different span-awareness behavior. `log::*`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this is true, as log actually forwards to tracing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referring to https://docs.rs/tracing-log/latest/tracing_log/, we need logTracer::init() to have logs and tracing connected. But in our project only the test file, test-context/src/flame.rs has this configuration for setup_global_subscriber.

Copy link
Copy Markdown
Contributor

@helio-frota helio-frota May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are using a tracing-log feature here: https://github.com/guacsec/trustify/blob/main/common/infrastructure/Cargo.toml#L33

log actually forwards to tracing

this explains why even using log::info here https://gist.github.com/helio-frota/171ac4ac8d362827e22b66ce165f32cf?permalink_comment_id=6121576#gistcomment-6121576, I was able to see the correlation with traces using opentelemetry-appender-tracing instead of opentelemetry-appender-log 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification @helio-frota! updated this section.


**Convention options**:

- **Option A — Use `tracing::` exclusively**: All logging MUST use `tracing::` macros
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option A sounds reasonable.


- **Option C — Keep as-is**: No convention. Inline values are acceptable with explanatory
comments.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option D: It depends. In general we should have Option A. However, there are cases where it causes confusion or has other side effects.

Assume the example from above. Having 13 replaced by a const value, would require to render the string every time used. Rather than having a const struct. This seems counter productive. It would be better to keep that 13 in the string, but document around it "why" there's a 13 in there.

Same for structs:

        ImporterConfiguration::Cve(CveImporter {
            common: CommonImporter {
                disabled: true,
                period: Duration::from_secs(300),
                description: Some(description.into()),
                labels: Default::default(),
            },
            source: DEFAULT_SOURCE_CVEPROJECT.into(),
            years: HashSet::default(),
            start_year,
        }),

Having DEFAULT_SOURCE_CVEPROJECT here, imported from another module. Only being used once. Doesn't make much sense to me. It would be clearer if that would be in the same file. Unless, it would indeed be re-used. Then it could be refactored.


| # | File | Line(s) | Description |
|---|------|---------|-------------|
| 1 | `modules/fundamental/src/vulnerability/service/mod.rs` | ~455-657 | `build_vulnerabilities_query_string` (~455-521) uses `format!()` to interpolate SQL fragments; `build_query` (~523-657) constructs sub-queries with `Statement::from_sql_and_values`, then `.to_string()`s them and concatenates with `UNION ALL` into a `Statement::from_string` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example might not be ideal. But it seems like a decent trade off handling the complexity. Having that replaced by something else, the question would be if the alternative is actually better,

There is no SQL injection in play here, as that's gets sorted out before.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

SeaORM's `UnionType` builder or a single parameterized CTE. `Statement::from_string`
is only acceptable for static SQL with no dynamic values.

- **Option B — Allow with review gate**: `Statement::from_string` with dynamic content
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option B: There should be a good reason to not use Option A, and a discussion around that. But to me the example shows a valid reason to deviate from the norm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

Copy link
Copy Markdown
Contributor Author

@mrrajan mrrajan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ctron for your review, I have added a new section for database and addressed the review comments. I have added a new section Preferred option: under each category based on the review.

Comment thread docs/adrs/00018-conventions-file.md Outdated
| **Entity Model Patterns** | ORM model conventions | `DeriveEntityModel`, relations, `Linked` structs |
| **Migration Patterns** | Database migration conventions | Idempotency guards, naming, raw SQL loading |
| **Rust Idioms** | Preferred Rust patterns | Type inference, iterator ownership, `.zip()`, capacity |
| **SeaORM Query Patterns** | ORM query conventions | `.is_in()`, chunking, `IntoIterator` parameters |
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - Removed

Comment thread docs/adrs/00018-conventions-file.md Outdated
| **Rust Idioms** | Preferred Rust patterns | Type inference, iterator ownership, `.zip()`, capacity |
| **SeaORM Query Patterns** | ORM query conventions | `.is_in()`, chunking, `IntoIterator` parameters |
| **Observability** | Tracing and instrumentation | `#[instrument]` usage, span conventions, error levels |
| **Shared Table Patterns** | Concurrent insert handling | Nested transaction duplicate-key and `.on_conflict` patterns |
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! We should have a dedicated database section.

Comment thread docs/adrs/00018-conventions-file.md Outdated
1. **Prescriptive, not descriptive**: each convention states what to do and what to avoid, with concrete code examples
2. **Derived from existing code**: conventions are extracted from established patterns in the codebase, not invented
3. **Minimal and actionable**: each entry should be short enough that a contributor (or AI tool) can apply it without reading surrounding prose
4. **Reference implementations**: point to specific files in the codebase as canonical examples
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! We should have just the examples not the references to the specific files. Rephrased to share canonical examples on the conventions file.

- Claude Code automatically loads `CONVENTIONS.md` from the repository root as part of its project context
- The file uses markdown with code blocks, making it parseable by any LLM
- Conventions are structured as clear rules with examples, optimizing for AI instruction-following
- When a `CLAUDE.md` file is present (for tool-specific configuration), `CONVENTIONS.md` complements it — `CONVENTIONS.md` focuses on language and framework patterns that apply regardless of the tool
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcery.ai applies generic best practices and CONVENTIONS.md captures project specific ones. So, project specific practices might called out by the review tools. We have to explore how to align review tools to use CONVENTIONS.md file as reference.

Comment thread docs/adrs/00018-conventions-file.md Outdated
SeaORM's `UnionType` builder or a single parameterized CTE. `Statement::from_string`
is only acceptable for static SQL with no dynamic values.

- **Option B — Allow with review gate**: `Statement::from_string` with dynamic content
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

Comment thread docs/adrs/00018-conventions-file.md Outdated

## Context

Trustify uses AI-assisted development workflows (Claude Code, Copilot, and similar tools). These tools perform best when they have access to explicit, machine-readable project conventions — coding patterns, naming rules, error-handling idioms, testing practices, and architectural norms.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! Rephrased to,

Trustify needs a single, explicit reference for coding patterns, naming rules, error-handling idioms, testing practices, and architectural norms. Contributors and reviewers use it during implementation and review; the same clarity also helps AI-assisted workflows (Claude Code, Copilot, and similar tools) when those tools load project context.


### AP-2: Unbounded Queries

**What it is**: Queries that fetch all rows from a table or relation without applying
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! lets limit only for the public API endpoints

| 1 | `modules/fundamental/src/vulnerability/model/details/mod.rs` | ~54-68 | All scores fetched via `.all(tx)`, then filtered with `.filter(\|s\| s.advisory_id == ...)` |
| 2 | `modules/fundamental/src/advisory/model/summary.rs` | ~40-67 | Bulk scores fetched, then O(advisories x vulns x scores) in-memory iteration |

**Convention options**:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree!

Comment thread docs/adrs/00018-conventions-file.md Outdated
and `tracing::instrument`/`tracing::Instrument` attributes, creating inconsistent
observability output.

**Why it matters**: `tracing` and `log` have different span-awareness behavior. `log::*`
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referring to https://docs.rs/tracing-log/latest/tracing_log/, we need logTracer::init() to have logs and tracing connected. But in our project only the test file, test-context/src/flame.rs has this configuration for setup_global_subscriber.

@mrrajan mrrajan force-pushed the TC-4289 branch 2 times, most recently from 00239e8 to 9c838c3 Compare May 18, 2026 13:40
mrrajan added 3 commits May 19, 2026 17:31
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
…ntions

Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
- **Option A — Extract shared logic into generics or traits**: When two or more modules
implement the same logic pattern (e.g., label CRUD, license filtering), extract it into
a shared generic function, trait, or macro parameterized by the entity type. Duplicated
logic blocks longer than ~20 lines are prohibited.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

informational only:

I quickly modified my personal duplicate code segment tool to focus on 20+ lines and found only these: https://gist.github.com/helio-frota/87f1a9ab12fe84480f2574bc7b92e8a7
( I'm skipping tests,test,entity,migration,benches )

Comment thread docs/adrs/00018-conventions-file.md Outdated
Copy link
Copy Markdown
Contributor

@ctron ctron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. With the exception that I still see the actual content (AP, …) My understanding was, that his would be removed from this ADR, but then added in another PR to the conventions file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mrrajan mrrajan requested a review from ctron May 28, 2026 09:36
@mrrajan mrrajan enabled auto-merge May 28, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants