Best practices

DOs and DONT's of common CakePHP problems.

Routing

Best to use the DashedRoute class (see conventions) as default one:
Router::defaultRouteClass('DashedRoute');
This way your URLs are `my-prefix/my-plugin/controller-name/action-name` whereas your URL contains the CamelCase variants:
'prefix' => 'my-prefix // unmodified
'plugin' => 'MyPlugin, // camelCased
'controller' => 'ControllerName', // camelCased
'action' => 'actionName' // camelBacked
The idea of CakePHP 3.x is to inflect internally as less as possible.

URLs

Use array URLs wherever possible, this saves you a lot of trouble once you actually want to customize the routing:
// URL /my-controller/my-action
echo $this->Html->link($title, ['controller' => 'MyController', 'action' => 'myAction']);
You can then alter the URLs via Routing and all those URLs change cleanly. The speed issue can be neglected compared to the advantages of the flexibility.

Don't sanitize the heck out your data

Use Sanitization wisely, and not blindly.
Not without reason the Sanitize class has been kicked out of the core files.

Sanitization is useful and necessary, when working with HTML content, that needs to be stripped of invalid/dangerous markup. But here it is best to use plugins specifically written for this job.
For most normal use cases, by using save(), SQL injections are already prevented. No need to modify the data upon save. Only use h() in the view to secure (stringish) output by escaping potentially dangerous chars:
echo h($entity->name);

Logging

By default the log streams catch all, even scoped logs that should only go to those scoped listeners. As a result they are duplicated.
So I would change the scopes to false here for all default listeners:
// in your app.php config
'Log' => [
	'debug' => [
		'scopes' => false,
	],
	'error' => [
		'scopes' => false,
	],
	...
],

Use AJAX wisely

Don't over-ajaxify your views. It can easily create complications and become error-prone. Use it wisely where it makes sense.
Also always try to provide a non-JS fallback solution in case the JS breaks or cannot work properly in some browsers. It might also be a good idea for search engines to properly pick up your site content (e.g. when using pagination and AJAX).

So the smart approach is: First code the non-JS functionality. And then you can add JS-functionality for it on top. In case the JS breaks, the non-js part can take over without users being unable to proceed.