.ai/principles/distilled/backend-ruby.md
attr_reader for public attributes only when accessed outside the class; maintain consistency for internal access.for_ prefix for scopes filtering by belongs_to associations (e.g., scope :for_project).with_ prefix for scopes using joins, includes, or filtering by has_one/has_many/boolean conditions.including_ prefix for scopes that eager-load associations via includes without changing the result set; use preload_ when loading multiple has_many associations or when a separate subquery is explicitly required.order_by_ prefix for scopes that apply order.CONSTANT = 'value'.freeze).has_many through: or has_one through: associations; overriding changes destroy() behavior and can cause data loss.Gitlab::Json in place of all calls to the default JSON class, .to_json, and similar methods.Gitlab::Json::LimitedEncoder when JSON output size must be bounded.Rails.logger; use a structured JSON logger instead.$stdout.puts, $stderr.puts, $stdout.print, $stderr.print, or equivalent STDOUT/STDERR calls in application code; use a structured JSON logger or existing wrapper methods (e.g., SystemCheck::Helpers) for Rake/CLI output.Gitlab::JsonLogger for new log files; call exclude_context! if the logger is used outside of a request context.logger.info(message: "...", project_id: id)).class attribute in structured log payloads; use Gitlab::Loggable and build_structured_payload to add it automatically._s and include duration in the key name (e.g., view_duration_s).Gitlab::ErrorTracking.track_exception or Gitlab::ErrorTracking.track_and_raise_exception with additional context parameters.labkit-ruby lib/labkit/fields.rb._(), s_(), or n_() helpers; use __(), s__(), n__() in JavaScript/Vue.safe_format with tag_pair in Ruby/HAML or GlSprintf in Vue.downcase or toLocaleLowerCase() on translatable strings; let translators control casing.|) to all UI strings to provide translator context; prefer granular subcategories over broad ones.%{named} placeholders (not %d positional) in singular strings to allow natural translation.n_/n__ only to select between plural forms of the same string, not to switch between entirely different strings.:base with a complete sentence rather than to a specific attribute when the message is a full sentence, to avoid Rails prepending the humanized attribute name.locale/gitlab.pot by running tooling/bin/gettext_extractor locale/gitlab.pot before pushing changes to translated strings.have_content(_('...'))); DO NOT hard-code translated strings.__() — externalization is mocked and expectations should use plain string literals.{} when multiple keys must reside on the same Redis shard (hash-tags for Redis Cluster compatibility).Gitlab::Redis::Cache only for truly ephemeral, regenerable data; always set a TTL.Gitlab::Redis::SharedState for data that must persist until its expiration; always set a TTL.Rails.cache for data that must be reliably persisted; use Gitlab::Redis::SharedState instead.RedisCommands::Recorder in tests to detect Redis N+1 call problems and assert expected call counts.Gitlab::EtagCaching::Router, set the polling interval header via Gitlab::PollingInterval.set_header, and invalidate ETags via Gitlab::EtagCaching::Store on resource changes./-/ scope./-/ scope, except where a Git client or other external software requires otherwise./o/:organization_path/*path) for organization-level resources.Gitlab::Routing.redirect_legacy_paths when adding /-/ scope to previously unscoped routes, and create a technical debt issue to remove deprecated routes in a later release.db:drop and db:test:prepare will fail if an active session is held.config.autoload_paths or Zeitwerk inflections) in config/initializers_before_autoloader instead of config/initializers.@param, @return) when documenting method arguments or return values.@return [void] and explicitly return nil at the end.app/assets in application code; use lib/assets for assets that must be accessed by application code but not served directly.expect_any_instance_of or allow_any_instance_of in RSpec; use expect_next_instance_of, allow_next_instance_of, expect_next_found_instance_of, or allow_next_found_instance_of instead.rescue Exception; rescue specific exception classes.:javascript filter).rubocop:todo (not rubocop:disable) for temporary inline disables, and link the follow-up issue or epic.gitlab-styles gem; if it only applies to the main GitLab application, add it to the GitLab repository.bundle exec rake rubocop:todo:generate rather than adding inline disables throughout the codebase.For the full picture, see: