Skip to content

Add architecture documentation covering analysis strategy and design decisions #60

@daviburg

Description

@daviburg

Summary

Create a Docs/architecture.md covering the LSP server's overall architecture, component relationships, analysis strategy, and key design decisions — especially the deliberate choice of syntax-only analysis in the diagnostics layer.

Background

The repo has a Docs/ directory with feature-level design docs (attribute completions, E2E testing) but no central architecture document. The README describes what the server does and its repository structure, but not why the architecture is the way it is. Key design decisions are currently captured only in code comments.

During the implementation of trigger payload validation (#42, PR #49), a significant question arose: should the diagnostics validators use Roslyn's semantic model (CSharpCompilation + SemanticModel) or syntax-only analysis (CSharpSyntaxTree.ParseText)?

The decision was syntax-only, for reasons that should be documented as an architecture decision record so future contributors don't re-investigate the same trade-off.

Proposed Content

1. Component Overview

  • Server architecture: handlers, validators, services, stores
  • VS Code extension: lifecycle, file watching, connection merging
  • SdkIndex: assembly analysis and metadata management
  • How components interact (handler → validator → SdkIndex flow)

2. Analysis Strategy: Syntax-Only vs. Semantic Model

Current state:

  • CodeLensHandler, HoverHandler, CompletionHandler, and TextDocumentSyncHandler create CSharpCompilation with SDK assembly references to get SemanticModel for symbol resolution
  • All diagnostic validators (AttributeValidator, ConnectionConfigValidator, TriggerPayloadValidator) use syntax-only analysis via CSharpSyntaxTree.ParseText — no compilation, no references

Design decision: diagnostics use syntax-only analysis

Factor Syntax-only Semantic model
Latency ~1-5ms per file ~50-500ms per file
Dependencies None — just parse text Must load all referenced assemblies
Failure modes Cannot fail Assembly resolution can fail silently
What it resolves Token text, tree structure Types, symbols, overloads, using resolution

Why syntax-only is the right choice for diagnostics:

  • Diagnostics run on every keystroke via LSP; latency budget is tight
  • The SDK's target audience writes straightforward Azure Functions code — using System.Text.Json; at the top, JsonSerializer.Deserialize<T>(body) in the method body
  • The edge cases where syntax-only fails (aliased types, local type shadows, same-named types in different namespaces) are addressed through heuristics: namespace-gated serializer detection, local type shadow scanning, global:: normalization, conditional access rejection
  • The heuristic cost is a few extra lines of code with ~0ms runtime impact, vs. 100ms+ for compilation on every keystroke
  • If shared compilation caching is implemented (Shared CompilationService — cache Roslyn compilations across handlers #46), the IDiagnosticValidator interface can be extended to optionally accept a semantic model — but this is an optimization, not a correctness requirement

Known limitations (acceptable):

  • Unqualified type matching by simple name may be incorrect if the SDK contains same-named types in different namespaces (none exist today)
  • CSDK204 checks naming convention (EndsWith("TriggerPayload")) rather than type hierarchy — intentional since syntax analysis cannot inspect inheritance

3. Diagnostic Validator Architecture

  • IDiagnosticValidator interface and registration
  • Priority chain in TriggerPayloadValidator (CSDK203 → CSDK201 → CSDK202 → match → CSDK204 → CSDK200)
  • Shared helpers in ValidatorHelpers
  • How validators integrate with SdkIndex

4. Cross-references

Acceptance Criteria

  • Docs/architecture.md exists with sections above
  • README links to it from the Repository Structure section
  • Design decisions are captured as ADR-style entries with context, decision, and consequences

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions