How to move fast while keeping the codebase clean?
See an article on Moving Fast With High Code Quality and clearPHP rules
- First an foremost keeping the codebase clean to avoid rewrites in long term
- Make profit!
- Other's interest in integration with your project (API)
- Move fast with development, don't use a tool if it slows down development
- Keep developer morale high
- Have a fixed time frame for paying back technical debt
- Think about the far future when making decisions today
- Software architecture ☀️ ☀️ ☀️
- Documented code design ☀️ ☀️
- Implementation (source code writing) ☀️
- Automatic and manual testing
- Periodic code review, security audit
Bits and bytes.
- Execute bit off
- Consistent indentation
- LF lineends
- UTF-8 encoding without BOM
- Trim trailing whitespaces
- Insert final newlines
- Non-ASCII characters (emoji, accented letter, signs)
LC_ALL=C grep -P '[\x80-\xFF]'
- Declare class, method and variable naming, consider PSRs
- Frameworks/CMS-s
- Packages/Libraries
- SaaS (Loco, Paperplane)
- Unified email, calendar, contacts API (Nylas)
- Development tools (Vagrant, Laragon)
- Testing tools (CI)
- Build and deployment tools (CD)
- Changelog (Headway)
- Application performance and error monitoring (Checkly)
- New feature or fix is ready and "works for me" → PR (new branch)
- → CI all green → dev branch
- → Previous feature approved → staging branch + deploy to staging server 🚢
- → Testing folks approve it → master branch
- → Wait for release → tag and deploy to production server 🚢
Commit checklist:
code, tests, changelog, commit message
with emojis 🐛,
issue link, watch CI (PULL_REQUEST_TEMPLATE.md
)
Release checklist: tag, build, deploy, announce (blog, email, Wiki)
- Catastrophe → hotfix branch + deploy to production server 🚢
- Alert (email, chat, SMS)
- Watch logs
- Merge changes to dev branch
What to include in continuous integration with 0% code coverage? (no unit tests, no functional test)
Use Docker containers for testing.
- Modern task runner (composer:scripts, consolidation/robo, npm:scripts, grunt, gulp)
- Parallel package installation (hirak/prestissimo)
- Git hook integration (phpro/grumphp)
- Parallel syntax check (php-parallel-lint)
- PSR-12-based coding style (phpcs)
- Warn on
TODO
andFIXME
: "Move it into issues!" (phpcs) - PHP Compatibility check (phpcompatibility/php-compatibility)
- PHPDoc checker
- Static analysis (phpstan, larastan, psalm, phan)
- Mess Detector (phpmd) rules: clean code, code size, controversial, design, naming, unused code
- Critical vulnerabilities in dependencies (sensiolabs/security-checker, roave/security-advisories, dependencies.io)
- Build assets (webpack)
- Metrics (phpmetrics, phploc, laravel-stats)
- PHPUnit
- Measure code coverage
- Codeception, Behat, KantuX
- Packaging
- Test deploy
Try Scrutinizer or Exakat on Debian
- Performance (Tideways, Blackfire)
- Security scanner (Netsparker, Ripstech, StackHawk, awesome-php-security)
- Laravel Analyzer
A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects,
or it SHOULD execute logic with side effects,
but SHOULD NOT do both.
- Separate frontend, backend, API, CLI, cron/queue
- Make your frontend a UI for your API ⭐
- Comment your source code like a travel guide!
- The less indentation the better code
- Leave environment settings to the server, and check environment (php-env-check.php)
- Move resource-intensive tasks to cron jobs/queues
- Store and calculate dates, times in UTC and display it in the user's timezone
- Develop simple maintenance tools (e.g. deploy, import, export) for the command line
- Autoloading (composer)
- DI containers
- Exception handling
- Logging
- ORM
- Database migration
- Application caching aka. object cache (PSR-6)
- HTTP communication (request, response, routes) and security (URL structure, WAF)
- Session handling (very long sessions, CSRF, session expiration UX: timer, warning, redirect, password input)
- Form handling, input validation, sanitization (UserFrontValidate->Request->BackendValidate->BusinessLogic->Response)
- Escaping (SQL, HTML, URL, JavaScript)
- Internationalization and localization (PHP, JavaScript, language, time zone, calendar, number formats and units, string collation), string translation (gettext, pseudo English)
- Content management: large pieces of markup, reusable content blocks
- Templating
- Authentication (Web Authentication API, client certificate, 2FA, password security, lock session to IP address, stronger authentication for admins)
- Ability of matching an event (uncaught exception) to a user ID or session
- User roles and capabilities
- Email addresses, composing and sending (maximum length, obfuscate email addresses, hidden field in form, mailcheck.js, plain text version, NeverBounce)
- Document generation (CSV, PDF, Excel, image)
- Image management (Cloudinary, https://blurha.sh/ )
- Maintenance mode switch and placeholder page (HTTP/503)
- Static asset management (building, versioning) and loading
- Search experience
- Keep A Changelog
- Analytics, visitor tracking (HEAP, Hotjar, Smartlook, Clicktale)
- Performance (application monitoring, New Relic)
- Error tracking: JavaScript, PHP, queue, cron (no overlapping)
- Document everything in
hosting.yml
- Declare PHP version, extensions, directives, functions and test them in php-env-check, run in composer.json:pre-install-cmd, PHP version and extensions also in composer.json:require
- Have an update policy for PHP, framework, packages
- Set environment variables (PHP-FPM pool,
.env
) - Publish Dockerfile of CI (GitLab Container Registry, Docker Hub)
- Build and deploy script (file permissions)
- Cron jobs and queues (check periodically, email sending and time consuming tasks,
catch SIGTERM on system shutdown
pcntl_signal(SIGTERM, 'signal_handler');
) - Generate sitemaps
- File change notification:
siteprotection.sh
- Manage and monitor application/config/route/view cache and sessions
- Run
git status
hourly - Report application log extract hourly (recipients)
- Rotate application log
- Move per-directory webserver configuration to vhost configuration
- Redirect removed routes, substitute missing images (URL-s)
- Use queuing MTA for fast email delivery (external SMTP is slow), bounce handling
- Include firewall/Fail2ban triggers at least for: 404-s, failed login attempts, hidden form fields (WAF)
- Host a honey pot
- Register to webmaster tools (Google, Bing, Yandex)
- Match production/staging/development/local environments (Docker, php-env-check)
- Environment examples: development, staging, beta, demo
- Different domain name (SLD)
- Disallowing robots.txt
- Different Apache configuration
- Different PHP extensions and directives (
opcache.validate_timestamps
) - Alternative email delivery
- Modified application configuration (environment name, debug logging)
- Change crypto salts, regenerate password hashes
- Disable/use another CDN
- Disable/switch to sandbox mode in 3rd party integrations (analytics, chat, performance monitoring, payment gateway)
- Disable automatic updates
- Stop cron jobs
- Visually distinguish non-production sites
- Change favicon to an animated GIF image
- Tag page title
<title>[STAGING] $page_title</title>
- Add a flashy line
#MainMavigation { border-top: 3px dashed magenta; }
- Surround/invert the company logo
#BrandLogo { outline: 3px dotted magenta; }
- Change background color of WordPress admin bar
- Logo and title
- Language selector
- News or marketing message
- "Remember me" checkbox
- "Forgot password" link
- Login and Sign up page linking each other
- Direct registration on login page: email field and signup button
- SSO
- Privacy Policy and Terms of Service links
- Support email and chat/open ticket
- Marketing message on "logged out" pages
- Analyze HTTP headers
- Browser check with JavaScript (proof-of-work)
- Client-side email address check
- Suspicious email address:
- company domain
- blocked domain (e.g. example.com, *.test)
- disposable address
- non-existent domain
- missing MX
- unresolvable MX
- Blocked usernames
- Force strong passwords:
- previously used
- on most common passwords list
- similarity to name, username or other user details
- length
- complexity
- xkcd password strength
- pwned password
- Provide 2FA (TOTP, SMS, email), encourage users to use KeePass
- Use Argon2 hashing
password_hash($pwd, PASSWORD_ARGON2I)
- Wipe the plaintext password from memory
- Login security: lock sessions to, and allow login from
- 1 IP address (IPv4, IPv6)
- In an IP range (e.g. a /24 network)
- Within 1 AS (autonomous system) thus inside an ISP
- Within multiple AS-es (mobile roaming)
- Within a country
- Within a region/timezone (multiple countries)
- Within a continent
- Same user agent strings
- Same device (user agent strings) with upgrades (device, OS, browser)
- Allow/deny multiple (how many?) sessions
- Session timeout
- Authorize IP address procedure
- Login notification
- New device notification
- Login logging or last successful login logging
- Inactive accounts
- Authentication as a Service
- Authentication system
- If you choose an identity provider search the web for its name plus "breach" "exploit" "security"
form
analyze HTTP requestform
hidden field in forminput
maximum lengthinput
mailcheck.jsinput
Suspicious email addressinput
NeverBounceoutput
obfuscate email addressesdelivery
prevent automatic responsesdelivery
detect bounce -> take action (stop sending, notify user or user's team)
List: https://www.g2.com/categories/help-desk
Multilingual support.
- Password reminder
- Ask for a new password
- Get help (see Logged in section)
- Suggest a password manager (avoid saving passwords to browser)
- Short video about password and cybersecurity
- Signing in on an old login page (reopened by the browser) with expired cookies
- Login to a specific page (inside the application) through the login page
- Custom messages on each failed login attempt, automatic redirect to password reminder page
- Open ticket
- Start online chat
- Search knowledge base (help articles)
- Take a screenshot
- Send attachments
- General feedback, bug reporting
- Record sessions
Have me on board: [email protected]
These lists are theory-free! All of them were real-life problems.