The open curriculum for learning web development
This report summarizes scanner evidence for TheOdinProject/curriculum, a JavaScript project with 13,196 GitHub stars. SynthScan examined 67,067 lines of code across 622 source files, recording 128 pattern matches distributed across 6 syntactic categories. The legacy adjusted pattern signal is 3.2 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 102 finding(s).
Are imports declared and package/API names verified? 26 finding(s).
Are wrappers, guards, and repeated structures necessary? 4 finding(s).
Which changes need focused human review? 128 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 128 distinct pattern matches across 6 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | advanced_html_css/responsive_design/project_homepage.md | 13 | #### Step 1: Set up and planning | COMMENT |
| LOW⚡ | advanced_html_css/responsive_design/project_homepage.md | 18 | #### Step 2: Gather assets | COMMENT |
| LOW⚡ | advanced_html_css/responsive_design/project_homepage.md | 25 | #### Step 3: Some tips! | COMMENT |
| LOW⚡ | advanced_html_css/responsive_design/project_homepage.md | 32 | #### Step 4: Give your feedback | COMMENT |
| LOW | nodeJS/express/installation_guides/postgresql/linux.md | 3 | #### Step 1: Make sure the system is up to date | COMMENT |
| LOW | nodeJS/express/installation_guides/postgresql/linux.md | 11 | #### Step 2: Install the PostgreSQL packages | COMMENT |
| LOW | nodeJS/express/installation_guides/postgresql/linux.md | 29 | #### Step 3: Setting up PostgreSQL | COMMENT |
| LOW | nodeJS/express/installation_guides/postgresql/macos.md | 3 | #### Step 1: Make sure the system is up to date | COMMENT |
| LOW | nodeJS/express/installation_guides/postgresql/macos.md | 14 | #### Step 2: Install PostgreSQL via PostgresApp | COMMENT |
| LOW⚡ | archive/installing_node.md | 14 | #### Step 0: Prerequisites | COMMENT |
| LOW⚡ | archive/installing_node.md | 21 | #### Step 1: Downloading and Installing `nvm` | COMMENT |
| LOW⚡ | archive/installing_node.md | 31 | #### Step 2: Initializing `nvm` | COMMENT |
| LOW | archive/installing_node.md | 88 | #### Step 1: Installation | COMMENT |
| LOW | archive/installing_node.md | 104 | #### Step 2: Setting the Node Version | COMMENT |
| LOW | …int/TOP004_lessonHeadings/tests/_guides/guide_valid.md | 5 | ### Step 1: Download software | COMMENT |
| LOW | …int/TOP004_lessonHeadings/tests/_guides/guide_valid.md | 13 | ### Step 2: Install | COMMENT |
| LOW | …t/TOP004_lessonHeadings/tests/_guides/guide_invalid.md | 5 | ### Step 1: Download | COMMENT |
| LOW⚡ | foundations/javascript_basics/loops_and_arrays.md | 56 | // Step 1: If the element is an even number | COMMENT |
| LOW⚡ | foundations/javascript_basics/loops_and_arrays.md | 58 | // Step 2: Multiply this number by three | COMMENT |
| LOW⚡ | foundations/javascript_basics/loops_and_arrays.md | 61 | // Step 3: Add the new number to the total | COMMENT |
| LOW | …tions/javascript_basics/project_rock_paper_scissors.md | 37 | #### Step 1: Setup the project structure | COMMENT |
| LOW⚡ | …tions/javascript_basics/project_rock_paper_scissors.md | 49 | #### Step 2: Write the logic to get the computer choice | COMMENT |
| LOW⚡ | …tions/javascript_basics/project_rock_paper_scissors.md | 59 | #### Step 3: Write the logic to get the human choice | COMMENT |
| LOW⚡ | …tions/javascript_basics/project_rock_paper_scissors.md | 69 | #### Step 4: Declare the players score variables | COMMENT |
| LOW⚡ | …tions/javascript_basics/project_rock_paper_scissors.md | 76 | #### Step 5: Write the logic to play a single round | COMMENT |
| LOW | …tions/javascript_basics/project_rock_paper_scissors.md | 99 | #### Step 6: Write the logic to play the entire game | COMMENT |
| LOW | …dations/javascript_basics/installation_guides/linux.md | 3 | #### Step 0: Prerequisites | COMMENT |
| LOW | …dations/javascript_basics/installation_guides/linux.md | 17 | #### Step 1: Downloading and installing nvm | COMMENT |
| LOW | …dations/javascript_basics/installation_guides/linux.md | 25 | #### Step 2: Initializing nvm | COMMENT |
| LOW⚡ | …installations/installation_guides/text_editors/wsl2.md | 3 | #### Step 1: Install VSCode | COMMENT |
| LOW⚡ | …installations/installation_guides/text_editors/wsl2.md | 9 | #### Step 2: Delete the installer file | COMMENT |
| LOW⚡ | …installations/installation_guides/text_editors/wsl2.md | 15 | #### Step 3: Install WSL Extension | COMMENT |
| LOW⚡ | …installations/installation_guides/text_editors/wsl2.md | 21 | #### Step 4: Ensure that WSL2 can correctly open VSCode | COMMENT |
| LOW | …nstallations/installation_guides/text_editors/linux.md | 3 | #### Step 1: Download VSCode | COMMENT |
| LOW | …nstallations/installation_guides/text_editors/linux.md | 11 | #### Step 2: Install VSCode | COMMENT |
| LOW | …nstallations/installation_guides/text_editors/linux.md | 51 | #### Step 3: Delete the installer file | COMMENT |
| LOW | …nstallations/installation_guides/text_editors/linux.md | 59 | #### Step 4: Using VSCode | COMMENT |
| LOW⚡ | …nstallations/installation_guides/text_editors/macos.md | 3 | #### Step 1: Download VSCode | COMMENT |
| LOW⚡ | …nstallations/installation_guides/text_editors/macos.md | 7 | #### Step 2: Install VSCode | COMMENT |
| LOW⚡ | …nstallations/installation_guides/text_editors/macos.md | 13 | #### Step 3: Delete the installer file | COMMENT |
| LOW⚡ | …nstallations/installation_guides/text_editors/macos.md | 19 | #### Step 4: Using VSCode | COMMENT |
| LOW | …ations/installations/installation_guides/linux/wsl2.md | 15 | ### Step 1: Installations | COMMENT |
| LOW | …allations/installation_guides/linux/virtual_machine.md | 5 | ### Step 1: Download VirtualBox and Xubuntu | COMMENT |
| LOW | …allations/installation_guides/linux/virtual_machine.md | 27 | ### Step 2: Install VirtualBox and set up Xubuntu | COMMENT |
| LOW | …allations/installation_guides/linux/virtual_machine.md | 116 | ### Step 3: Begin Xubuntu installation | COMMENT |
| LOW | …allations/installation_guides/linux/virtual_machine.md | 192 | ### Step 4: Updates and Guest Additions | COMMENT |
| LOW | …allations/installation_guides/linux/virtual_machine.md | 272 | ### Step 4: Understand your new VM | COMMENT |
| LOW⚡ | …s/installations/installation_guides/linux/dual_boot.md | 7 | ### Step 0: BitLocker | COMMENT |
| LOW⚡ | …s/installations/installation_guides/linux/dual_boot.md | 17 | ### Step 1: Download Ubuntu | COMMENT |
| LOW⚡ | …s/installations/installation_guides/linux/dual_boot.md | 21 | ### Step 2: Create a bootable flash drive | COMMENT |
| LOW⚡ | …s/installations/installation_guides/linux/dual_boot.md | 25 | ### Step 3: Install Ubuntu | COMMENT |
| LOW⚡ | …tions/installations/installation_guides/chrome/wsl2.md | 5 | #### Step 1: Download Google Chrome | COMMENT |
| LOW⚡ | …tions/installations/installation_guides/chrome/wsl2.md | 10 | #### Step 2: Install Google Chrome | COMMENT |
| LOW⚡ | …tions/installations/installation_guides/chrome/wsl2.md | 15 | #### Step 3: Using Google Chrome | COMMENT |
| LOW | …ions/installations/installation_guides/chrome/linux.md | 3 | #### Step 1: Download Google Chrome | COMMENT |
| LOW | …ions/installations/installation_guides/chrome/linux.md | 20 | #### Step 2: Install Google Chrome | COMMENT |
| LOW | …ions/installations/installation_guides/chrome/linux.md | 50 | #### Step 3: Delete the installer file | COMMENT |
| LOW | …ions/installations/installation_guides/chrome/linux.md | 56 | #### Step 4: Using Google Chrome | COMMENT |
| LOW⚡ | …ions/installations/installation_guides/chrome/macos.md | 3 | #### Step 1: Download Google Chrome | COMMENT |
| LOW⚡ | …ions/installations/installation_guides/chrome/macos.md | 8 | #### Step 2: Install Google Chrome | COMMENT |
| 32 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | nodeJS/express/forms_and_data_handling.md | 24 | <input placeholder="John Doe" type="text" name="fullName" id="fullName"> | CODE |
| LOW | databases/databases/databases_and_sql.md | 60 | INSERT INTO users (name, email) VALUES ('foobar', 'foo@bar.com'); | CODE |
| LOW | databases/databases/databases_and_sql.md | 72 | WHERE email='foo@bar.com'; | CODE |
| LOW | archive/html_css/project_design.md | 23 | 1. Add in some meaningless text to those divs (whether copied from the homepage or a [lorem ipsum generator](http://gene | CODE |
| LOW | …dations/html_css/html_foundations/working_with_text.md | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor | CODE |
| LOW | …dations/html_css/html_foundations/working_with_text.md | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor | CODE |
| LOW | …dations/html_css/html_foundations/working_with_text.md | 209 | 1. To get some practice working with text in HTML, create a plain blog article page which uses different headings, uses | CODE |
| LOW | ruby_on_rails/forms_and_authentication/form_basics.md | 42 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJa87aK1OpXfjojryBk2Db6thv0K3bSZeYTuW8hF4Ns=", "email"=>"foo@bar.com" | CODE |
| LOW | ruby_on_rails/forms_and_authentication/form_basics.md | 63 | Now another thing we talked about in the controller section was nesting data. You'll often want to tuck submitted data n | CODE |
| LOW | ruby_on_rails/forms_and_authentication/form_basics.md | 78 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJa87aK1OpXfjojryBk2Db6thv0K3bSZeYTuW8hF4Ns=", "user"=>{"first_name"= | CODE |
| LOW | ruby_on_rails/forms_and_authentication/project_forms.md | 58 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJa87aK1OpXfjojryBk2Db6thv0K3bSZeYTuW8hF4Ns=", "email"=>"foo@bar.com | CODE |
| LOW | ruby_on_rails/forms_and_authentication/project_forms.md | 67 | Parameters: {"authenticity_token"=>"WUaJBOpLhFo3Mt2vlEmPQ93zMv53sDk6WFzZ2YJJQ0M=", "username"=>"foobar", "email"=>"fo | CODE |
| LOW | ruby_on_rails/forms_and_authentication/project_forms.md | 94 | Parameters: {"authenticity_token" => "WUaJBOpLhFo3Mt2vlEmPQ93zMv53sDk6WFzZ2YJJQ0M=", "user" =>{ "username" => "foobar | CODE |
| LOW | …vanced_forms_and_activerecord/active_record_queries.md | 35 | [#<User id: 1, email: "foo@bar.com">] | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 89 | 1. `User.where(email: "foo@bar.com")` | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 90 | 1. `User.where("email" => "foo@bar.com")` | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 91 | 1. `User.where("email = 'foo@bar.com'")` | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 92 | 1. `User.where("email = ?", "foo@bar.com")` | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 93 | 1. `User.where("users.email" => "foo@bar.com")` | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 101 | The key thing to note is that `#find` returns the actual record while `#where` returns an `ActiveRecord::Relation` which | CODE |
| LOW⚡ | …vanced_forms_and_activerecord/active_record_queries.md | 103 | `#find_by` is a really neat method that basically lets you build your own finder method. It's an alternative to using `# | CODE |
| LOW⚡ | …tml_css/intermediate_css_concepts/custom_properties.md | 87 | <p class="cool-paragraph">Lorem ipsum dolor sit amet.</p> | CODE |
| LOW⚡ | …tml_css/intermediate_css_concepts/custom_properties.md | 87 | <p class="cool-paragraph">Lorem ipsum dolor sit amet.</p> | CODE |
| LOW⚡ | …tml_css/intermediate_css_concepts/custom_properties.md | 89 | <p class="exciting-paragraph">Lorem ipsum dolor sit amet.</p> | CODE |
| LOW⚡ | …tml_css/intermediate_css_concepts/custom_properties.md | 89 | <p class="exciting-paragraph">Lorem ipsum dolor sit amet.</p> | CODE |
| LOW | ruby/basic_ruby/methods.md | 179 | "Jane Doe" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | …ails/advanced_forms_and_activerecord/forms_advanced.md | 122 | There are a couple new aspects to this process. With `#fields_for`, you basically create a form within a form (which sh | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …kdownlint/TOP012_headingLevels/TOP012_headingLevels.js | 26 | function getAssignmentPanelLineRange(lines) { | CODE |
| LOW | …_defaultSectionContent/TOP003_defaultSectionContent.js | 165 | function getAssignmentSectionErrors(sectionTokens) { | CODE |
| LOW | …P013_descriptiveHeadings/TOP013_descriptiveHeadings.js | 31 | function removeTrailingPunctuation(text) { | CODE |
| LOW | foundations/javascript_basics/clean_code.md | 244 | function extractTextWithinBrackets(text) { | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | ruby_on_rails/rails_sprinkles/turbo.md | 344 | - Check out this tutorial on [Turbo Frames and Turbo Stream](https://www.hotrails.dev/turbo-rails/turbo-frames-and-turbo | CODE |
| MEDIUM | ruby/testing_ruby_with_rspec/introduction_to_rspec.md | 196 | At this point, refactoring isn't necessary. The `#add` method is essentially a one line method. As you progress in your | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ruby_on_rails/rails_basics/routing.md | 141 | # code to grab the proper Post so it can be | COMMENT |
| LOW | ruby/basic_ruby/nested_collections.md | 161 | end | COMMENT |
| LOW | ruby/basic_ruby/nested_collections.md | 181 | teacher_mailboxes.flatten.each do |teacher| | COMMENT |