Candidate A: what Rateo prints

The Rateo engine, run on Candidate A, a Laravel 13 take home assignment. Every number below comes from the engine's output, which is linked in full at the end.

Names that identify the project or its authors are replaced by stand ins of the same length, so every number on this page is what the engine prints for the original code.

License
MIT
Engine
2026.10.12
Rules
2026.10.7-provisional
Ran on
2026-10-16
Zip SHA-256
1b09335f7d67bbb055b533c9f374ba48b3ee423f4ceb1286975cfd3a33b6c8e9
Command
build/rateo candidate-a --cache=none
from the folder the zip was extracted into

At a glance

Files analysed
81
Findings
22
Errors
0
Warnings
22

platform PHP: Laravel (composer.json requires laravel/framework; artisan)

not analysed: 68 files (Markdown 37, Ignore List 13, JSON 5, Shell 2, ApacheConf 1, EditorConfig 1, Git Attributes 1, NEON 1, NPM Config 1, SQL 1, Text 1, XML 1, YAML 1, ico 1, robots.txt 1)

What it checks

Checked today

  • Size: long files, classes and functions
  • Complexity: functions and file level code with too many paths
  • Naming: mixed styles in a file, generic, one letter and very long names
  • Duplication: blocks repeated inside a file, even with renamed variables
  • Security: eval, shell commands, unserialize and includes built from data, SQL with values pasted in, request input never sanitized, output never escaped

Not checked yet

  • Laravel rules: thin controllers, no queries in views, validation in form requests
  • Security checks: hard coded secrets
  • Tests: whether there are any, and how much test code there is next to the app code
  • Repository hygiene: .gitignore, a lockfile, no committed vendor/ or .env, a README with setup steps

The security checks read one call or statement at a time and never follow a value through variables, so they show where to look, not proof of a hole. A long or branchy function is common in a large, mature project; each finding is a place worth a closer look, not a judgement of the code as a whole.

Findings by check

CheckLimits (warning / error)WarningsErrorsTotal
Size
length.function
A function or method body with too many lines
body lines: above 50 / above 100101
Duplication
duplication.repeated_block
A block repeated later in the same file, even with its names changed
tokens: 50 or more / never an error14014
Naming
naming.inconsistent_style
A name in a different style from most names of its kind in the file
style: differs from the file's main style (groups of 3 or more names) / never an error101
naming.generic
A local variable or parameter with a name that says nothing, like $data or $tmp
name: on the list of 16 generic names / never an error202
naming.too_long
A very long name
characters: above 30 / never an error303

A value equal to a limit is no finding.

Where they concentrate

The 10 files with the most findings

FileFindings
candidate-a/tests/Feature/AppointmentControllerTest.php9
candidate-a/config/database.php4
candidate-a/tests/Feature/FreeSlotControllerTest.php3
candidate-a/app/Data/FreeSlot.php1
candidate-a/app/Rules/EndsAtFitsSlotDuration.php1
candidate-a/app/Rules/SameDayAs.php1
candidate-a/app/Services/FreeSlotService.php1
candidate-a/database/seeders/DatabaseSeeder.php1
candidate-a/public/index.php1

Findings per top level folder

FolderFindings
tests/12
app/4
config/4
database/1
public/1

Grades

AOverall: 95 out of 100
3 of 5 metrics graded

round((3 × 100Complexity+2 × 85Duplication+1 × 98Naming) ÷ 6) = 95

Weights: Complexity 3, Duplication 2, Naming 1. Size and Security are not graded, so they are left out and the other weights are rescaled.

A summary of the grades below, which you can recompute. Not a pass mark, and not a hiring decision.

The table shows how many files got each letter. A file with nothing to judge for a metric gets no grade for it. The whole project column pools every file's grade, so a file with more code to judge counts for more. The overall above weights those whole project grades. In the full engine output these are CodeLength, CyclomaticComplexity, Duplication, NamingConvention and Security.

MetricABCDFNot gradedWhole project
SizeFindings only, no grade
Complexity71000010100 A
Duplication701002885 B
Naming5711002298 A
SecurityFindings only, no grade

Security

Findings only, never a grade: a wrong security grade would hurt more than none. A finding the author already silenced with a phpcs comment counts as suppressed, apart from Errors and Warnings, so you can tell reviewed code from unreviewed code.

CheckErrorsWarningsSuppressed
security.eval
A call to eval, which runs a string as PHP code
000
security.shell
A shell command built from data
000
security.unserialize
unserialize without allowed_classes => false
000
security.include_variable
An include or require whose path is built from a variable
010
security.extract_input
extract of request input, which turns its fields into variables
000
security.sql_interpolated
SQL with a value pasted in instead of passed through prepare
000
security.unescaped_output
An echo, print or <?= of a value that is not escaped
000
security.unsanitized_input
A request input read with no sanitizer around it
000

Where to look first

Up to 10 places, Errors first, then Warnings, then by path and line. An Error means request input reaches the call or the output directly.

#1 candidate-a/public/index.php:10

  • Warning security.include_variable

    require of `$maintenance` builds the file path from data; a crafted path can load another file

10 require $maintenance;
11}
12
13// Register the Composer autoloader...
14require __DIR__.'/../vendor/autoload.php';
15
16// Bootstrap Laravel and handle the request...
17/** @var Application $app */
18$app = require_once __DIR__.'/../bootstrap/app.php';
19

The 10 worst places

Security findings are listed in their own section above. A place is a file and a line, so a function that is both too long and too complex shows once, with both findings. Places rank by their worst finding: errors first, then by how far the measured value goes beyond its limit. Each message is the engine's own, word for word.

#1 candidate-a/config/database.php:68

  • Warning duplication.repeated_block

    lines 68 to 87 repeat lines 48 to 67 (190 tokens); repeated code has to be fixed in every copy

68 'driver' => 'mariadb',
69 'url' => env('DB_URL'),
70 'host' => env('DB_HOST', '127.0.0.1'),
71 'port' => env('DB_PORT', '3306'),
72 'database' => env('DB_DATABASE', 'laravel'),
73 'username' => env('DB_USERNAME', 'root'),
74 'password' => env('DB_PASSWORD', ''),
75 'unix_socket' => env('DB_SOCKET', ''),
76 'charset' => env('DB_CHARSET', 'utf8mb4'),
77 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),

#2 candidate-a/tests/Feature/AppointmentControllerTest.php:549

  • Warning duplication.repeated_block

    lines 549 to 568 repeat lines 527 to 546 (108 tokens); repeated code has to be fixed in every copy

549 $appointment = appointmentStartingAt($startTime)->confirmed()->create();
550
551 $response = $this->postJson(route('appointments.cancel', $appointment), validCancelPayload());
552
553 $appointment->refresh();
554
555 $response
556 ->assertOk()
557 ->assertExactJson([
558 'data' => appointmentResourceMap($appointment),

#3 candidate-a/tests/Feature/AppointmentControllerTest.php:226

  • Warning duplication.repeated_block

    lines 226 to 237 repeat lines 206 to 217 (88 tokens); repeated code has to be fixed in every copy

226 test('returns the second page of appointments', function () {
227 $start = CarbonImmutable::createFromFormat(ApiDateTime::FORMAT, '2026-09-03 08:00:00');
228
229 Appointment::factory()
230 ->count(16)
231 ->sequence(fn ($sequence) => [
232 'start_time' => $start->addMinutes($sequence->index * 30),
233 'end_time' => $start->addMinutes($sequence->index * 30 + 30),
234 ])
235 ->create();

#4 candidate-a/tests/Feature/AppointmentControllerTest.php:484

  • Warning duplication.repeated_block

    lines 484 to 502 repeat lines 439 to 457 (83 tokens); repeated code has to be fixed in every copy

484 $response = $this->postJson(route('appointments.complete', $appointment));
485
486 $appointment->refresh();
487
488 $response
489 ->assertOk()
490 ->assertExactJson([
491 'data' => appointmentResourceMap($appointment),
492 ]);
493

#5 candidate-a/config/database.php:106

  • Warning duplication.repeated_block

    lines 106 to 112 repeat lines 91 to 97 (75 tokens); repeated code has to be fixed in every copy

106 'port' => env('DB_PORT', '1433'),
107 'database' => env('DB_DATABASE', 'laravel'),
108 'username' => env('DB_USERNAME', 'root'),
109 'password' => env('DB_PASSWORD', ''),
110 'charset' => env('DB_CHARSET', 'utf8'),
111 'prefix' => '',
112 'prefix_indexes' => true,
113 // 'encrypt' => env('DB_ENCRYPT', 'yes'),
114 // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
115 ],

#6 candidate-a/config/database.php:169

  • Warning duplication.repeated_block

    lines 169 to 175 repeat lines 156 to 162 (73 tokens); repeated code has to be fixed in every copy

169 'cache' => [
170 'url' => env('REDIS_URL'),
171 'host' => env('REDIS_HOST', '127.0.0.1'),
172 'username' => env('REDIS_USERNAME'),
173 'password' => env('REDIS_PASSWORD'),
174 'port' => env('REDIS_PORT', '6379'),
175 'database' => env('REDIS_CACHE_DB', '1'),
176 'max_retries' => env('REDIS_MAX_RETRIES', 3),
177 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
178 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),

#7 candidate-a/tests/Feature/AppointmentControllerTest.php:514

  • Warning duplication.repeated_block

    lines 514 to 520 repeat lines 469 to 475 (71 tokens); repeated code has to be fixed in every copy

514 'pending' => fn () => Appointment::factory()->pending()->create(),
515 'cancelled' => fn () => Appointment::factory()->cancelled()->create(),
516 'completed' => fn () => Appointment::factory()->completed()->create(),
517 ]);
518
519 test('returns 404 when the appointment does not exist', function () {
520 $this->postJson(route('appointments.complete', 1))
521 ->assertNotFound();
522 });
523});

#8 candidate-a/tests/Feature/AppointmentControllerTest.php:529

  • Warning duplication.repeated_block

    lines 529 to 543 repeat lines 439 to 453 (71 tokens); repeated code has to be fixed in every copy

529 $response = $this->postJson(route('appointments.cancel', $appointment), validCancelPayload());
530
531 $appointment->refresh();
532
533 $response
534 ->assertOk()
535 ->assertExactJson([
536 'data' => appointmentResourceMap($appointment),
537 ]);
538

#9 candidate-a/tests/Feature/FreeSlotControllerTest.php:178

  • Warning duplication.repeated_block

    lines 178 to 186 repeat lines 157 to 165 (65 tokens); repeated code has to be fixed in every copy

178 Appointment::factory()->cancelled()->create([
179 'doctor_id' => $doctor->id,
180 'start_time' => '2026-09-03 09:00:00',
181 'end_time' => '2026-09-03 09:30:00',
182 ]);
183
184 $this->getJson(route('free-slots.index', ['doctor_id' => $doctor->id]))
185 ->assertOk()
186 ->assertJsonPath('data', morningFreeSlotMaps($doctor->id));
187 });

#10 candidate-a/config/database.php:175

  • Warning duplication.repeated_block

    lines 175 to 180 repeat lines 162 to 167 (59 tokens); repeated code has to be fixed in every copy

175 'database' => env('REDIS_CACHE_DB', '1'),
176 'max_retries' => env('REDIS_MAX_RETRIES', 3),
177 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
178 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
179 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
180 ],
181
182 ],
183
184];

Full engine output

All 22 findings, exactly as the engine printed them: laravel-candidate-a.txt (plain text).