AI coding assistant guidance for Canvas LMS.
docker compose up # Start services
docker compose run --rm web bash # Dev shell
yarn build:watch # Frontend dev mode| Task | Command |
|---|---|
| Build | yarn build (all), yarn build:watch (dev) |
| Test JS | yarn test, yarn test:vitest, yarn test:watch |
| Test Ruby | bin/rspec |
| Lint | yarn lint (JS), bin/rubocop (Ruby), yarn check:biome |
| Type Check | yarn check:ts |
| Webpack | yarn webpack-development (build), yarn webpack (watch) |
ui/- React components & shared packagesapp/- Rails MVC (controllers, models, views)packages/- Shared NPM packagesgems/plugins/- Canvas plugins (account_reports, analytics, etc.)lib/- Ruby business logic
- Multi-tenancy via Account hierarchies
- Database sharding with Switchman gem
- Plugin system in
gems/plugins/ - LTI integrations for external tools
- Brandable CSS theming (
yarn build:css) - Feature flags for gradual rollouts
- Any commands that use yarn, rake, bundle, or rails should be run inside the web container.
- Update packages: Edit package.json, run
docker_yarnfunction - Access Rails console:
docker compose run --rm web rails c - Database operations run inside containers
- JS testing guide:
doc/ui/testing_javascript.md - Run specific frontend tests:
yarn test path/to/test - Run specific RSpec tests:
bin/rspec path/to/test:<line_number> - Coverage:
yarn test:coverage
- Keep each line in commit messages under 60 characters
- Keep it short
- Provide the why behind the change
- Never touch
Gemfile*.lockfiles directly - Run
BUNDLE_LOCKFILE=active bundle outdatedto find the list of outdated gems. Keep this list in memory so you don't have to keep running it, since it is a relatively slow command. - Run
bundle update --conservative <gem_name>to update individual gems - Run
bundle installone more time to ensure all lockfiles are in sync - Commit the changes, with a commit message of
bundle update <gem_name>(you don't need to include the conservative flag in the commit message). Be sure to check for changes inGemfile*.lock,Gemfile.d/*.lock, andgems/*/Gemfile*.lock. - Some groups of gems can be updated as a group:
aws*google*- Rails:
action*,active*,rack*,rails,railties, andzeitwerk-- exceptactive_model_serializers datadogand its dependencies that aren't shared with other gems, such aslibdatadogfaraday*redis*rspec*rubocop*(and their dependencies that aren't shared with other gems, such asast)ruby-lsp*sentry*
- All other gems should be updated and committed independently.
- The commit message for a group of gems should be the base name without the wildcard as the "gem name", or
railsfor the Rails group. - Look in
Gemfile.lockto determine a gem's dependencies - they're indented one level deeper than the gem that depends on them in eachspecssection. - Don't attempt to update any gems that already have an exact version requirement on them.
- Don't bother updating
sorbet-runtimefor patch version changes. - Do the rubocop group last, after all other groups and individual gems, since it will likely have new offenses that will need to be resolved.
- If the gem is references by any file in
gems/plugins/*/*.gemspecwith an exact pin, it will need to be updated by changing the exact pin in the gemspec, then runningBUNDLE_LOCKFILE=active bundle install. You still need to run a barebundle installafterwards to ensure the main lockfile and any child lockfiles stay in sync.
Squashing migrations is the process of going through individual migrations in db/migrate by date, and "squashing" them into the InitCanvasDb migration, then deleting the original.
change_tableblocks that add new structures can be moved into the correspondingcreate_tableblock.- Individual DDL statements such as
add_index,add_reference, etc. should also be moved to the correspondingcreate_tableblock, and modified as appropriate if their arguments differ. remove_-style statements should result in the removal of the correspondingadd_-style structure fromInitCanvasDb. Check the model file for any removed columns, and if the column has been ignored there, remove it from the list. Remove theignored_columnsline completely if the list is empty.create_tableblocks should be moved intoInitCanvasDbcompletely, putting it into its properly alphabetized position (using the non-plural form of the table name, so that for examplediscussion_topicsis placed beforediscussion_topic_replies)set_replication_identitycalls are moved into theSetReplicaIdentiesmigration, in their same alphabetized position.- Any options (such as
algorithm: :concurrently,if_not_exists: true,validate: false,validate_constraint) used to make the original migration idempotent are not necessary inInitCanvasDb, and should be removed. - Options that are already the default should not be specified:
default: nilnull: trueindex: trueont.referencescallsindex: falseon non-reference column addition calls
- Keep the statements within
create_tableblocks organized, with a blank line between each section:- Column additions (including
t.timestamps) are the first section, preserving their original order they were added - Additional constraints are the next section, preserving their original order they were added
- Additional indexes are the final section, preserving their original order they were added, with the exception that the
t.replica_identity_indexis first.
- Column additions (including
- If the migration queues a
DataFixup, find the file defining it, and any associated spec file. If the DataFixup is not reference by any other migration, just delete the spec file, theDataFixupfile, and the migration file. create_initial_partitionscalls can be squashed into theCreateInitialPartitionsmigration.- Before making any modifications, reset the test database with
RAILS_ENV=test bin/rake db:test:reset, and then store a copy of the structure to a temporary file for later validation:pg_dump -s --restrict-key=MQTD3FxKJiJ5XiNN2cfyqy9ctUI0Tt9i3SWn8wZ7l2dYLJGctear9gqS1IRbdO5 canvas_test > original.sql - After making modifications, reset the test database again, dump it to a separate temporary file, and confirm that the structure has not changed by running
diff -u original.sql modified.sql. The output should be empty. Exceptions are allowed if the order of columns has changed, because a column that was squashed is now earlier in the table than a column that is added in migration in gems/plugins//db/migrate/. - Finally, alter
ValidateMigrationIntegrityby replacing the timestamp inlast_squashed_migration_versionwith the value from the last deleted migration, and increment the version number in the filename. - Be sure to run
script/rlint -aafterwards to fix any formatting issues.
Some users may run Canvas differently, so consider these useful default suggestions for starting and interacting with Canvas if no other methods have been specified.