<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <link href="http://pubsubhubbub.appspot.com/" rel="hub"/>
  <link href="https://f43.me/matthieu-napolis-blog.xml" rel="self"/>
  <title>Matthieu Napoli&amp;#039;s blog</title>
  <subtitle/>
  <link href="http://mnapoli.fr"/>
  <updated>2026-03-13T05:55:38+01:00</updated>
  <id>http://mnapoli.fr/</id>
  <author>
    <name>Matthieu Napoli&amp;#039;s blog</name>
  </author>
  <generator>f43.me</generator>
  <entry>
    <id>https://mnapoli.fr/optimizing-laravel-aws-lambda/</id>
    <title><![CDATA[Optimizing Laravel cold starts on AWS Lambda]]></title>
    <summary><![CDATA[<p>This article is based on my notes after experimenting with caching as much as possible before deploying a Laravel application to AWS Lambda using <a href="https://bref.sh">Bref</a>. The goal was to optimize AWS Lambda cold starts, without slowing down warm invocations.</p><h2>Caching Laravel configuration, routes, and more</h2><p>Bref provides built-in support for Laravel via a package. By default, this integration will cache the Laravel configuration on startup, i.e. on AWS Lambda cold start. That allows making subsequent requests faster.</p><p>This is the most basic step to optimizing Laravel. Laravel can "cache" many things (config, routes, etc.), and these are often done on deployment before traffic reaches the application.</p><p>We can replicate that with Bref, but the main thing to keep in mind is to do these caching operations in an environment as close to production as possible. Indeed, Laravel will sometimes <strong>hardcode absolute paths</strong> or even environment variables (if you cache the config) in the cached configuration.</p><p>To solve that, we need to run caching commands in Docker, using the Bref-provided container images.</p><p>One solution is to run Laravel commands like <code>route:cache</code> in a Bref container by mounting the application in <code>/var/task</code> (the path where the app runs in Lambda):</p><pre class="language-shell hljs shell" data-lang="shell">docker run --rm -it --entrypoint=php -v $(pwd):/var/task bref/php-84:2 artisan route:cache
</pre><p>Another approach would be to deploy the application as a container image instead of a zip file. In that case, we can build the image entirely the way we want to. Here’s an example:</p><pre class="language-dockerfile hljs dockerfile" data-lang="dockerfile"># syntax=docker/dockerfile:1.7
FROM bref/php-84:2
# Copy the application code
COPY --link . /var/task
# Clear any cached config that might reference local paths
RUN php artisan config:clear &amp;&amp; php artisan route:clear &amp;&amp; php artisan view:clear
RUN php artisan optimize
# The views are compiled in /tmp/storage/framework/views, let's copy them to `storage/framework/views` so that they are persisted
RUN mkdir -p storage/framework/views \
    &amp;&amp; cp -r /tmp/storage/framework/views/* storage/framework/views/ \
    &amp;&amp; rm -rf /tmp/*
# Make sure PHP on Lambda will be able to read these files
RUN chmod -R 777 storage/framework/views
RUN chmod -R 777 bootstrap/cache
</pre><p>One important thing to note if you want to compile the configuration before deploying: all environment variables will be hardcoded in the cached configuration.</p><p>That means you must have <strong>production</strong> environment variables set during that build. One way is to create the <code>.env</code> file with production secrets and copy that with the application in the container image. Then, <code>php artisan config:cache</code> (or <code>optimize</code>) will be able to cache those variables. If you do this, keep in mind that the container image contains production secrets, treat it appropriately (for example by tightening permissions to ECR).</p><h2>Pre-warming the opcode cache</h2><p>Another option I explored was pre-compiling PHP's opcode cache before deploying. That would enable PHP-FPM, on startup, to start with a partially warm opcache (avoiding reading PHP files from disk, parsing them, and compiling them on the fly).</p><p>Usually, PHP's opcode cache is stored in memory. But it can also be stored to disk during deployment and loaded from these files on startup in Lambda.</p><p>This approach is trickier for several reasons:</p><ol><li>PHP's opcode cache is 100% tied to the exact PHP version, extensions, config, etc. and not portable between systems</li>
<li>deploying PHP's opcode cache makes the deployed archive bigger, which slows down cold starts</li>
<li>if the opcode cache is deployed with the app as files, these are mounted as read-only in Lambda and PHP does not like that</li>
<li>you need a way to trigger opcache compilation to disk</li>
</ol><p>Let's go through these challenges:</p><blockquote>
<p>1. PHP's opcode cache is 100% tied to the exact PHP version, extensions, config, etc. and not portable between systems</p>
</blockquote><p>That can be solved by using the same container image during deployment as during production, which we already did in the previous section. So this is solved if you involve Docker.</p><blockquote>
<p>2. deploying PHP's opcode cache makes the deployed archive bigger, which slows down cold starts</p>
</blockquote><p>That is definitely true if you deploy with zip files, which is the default with Bref. Don't try this with zip deployments because the extra MB of opcode caches will cancel all your efforts.</p><p>However, when deploying with container images, AWS Lambda performs optimizations (there's a <a href="https://arxiv.org/abs/2305.13162">white paper</a> about it) and essentially streams the filesystem to Lambda as files are read. In other words, it will only load the files you actually read. This means we can add more files to the image and cold starts will not necessarily slow down.</p><p>In my experience, it was a balance between how much you compile and the resulting cold start times. For example, I tried compiling every single PHP file, but this ended up making cold starts worse. This was counter-intuitive: I expected PHP to read the <code>.bin</code> file instead of the <code>.php</code> one, so things could only be faster.</p><p>But when I compiled every PHP file and removed the original <code>.php</code> files from the container image, PHP crashed completely. For some reason it needed the original PHP files, and I believe that’s why cold starts would not improve 100% of the time. I know there are options to make opcache <em>not</em> read PHP files, and I tried to use all of them. But PHP always needed the original files no matter what. It might be something I missed, or it might be that "opcache from files" behaves differently than "opcache from memory". Feel free to continue my experiments and prove me wrong, I’d love that!</p><p>In the end, I found a good balance by compiling only the files in these directories (this was a Laravel app):</p><pre class="language-dts hljs dts" data-lang="dts">bootstrap
app
storage/framework/views
vendor/composer
vendor/bref
vendor/symfony/http-foundation
vendor/symfony/http-kernel
vendor/psr
</pre><p>You can use this as a starting point. I aimed at compiling the files that I knew would be used for sure for any HTTP request.</p><blockquote>
<p>3. if the opcode cache is deployed with the app as files, these are mounted as read-only in Lambda and PHP does not like that</p>
</blockquote><p>I explored one approach:</p><ul><li>set PHP's opcache directory (on disk) to <code>/tmp/opcache</code>, since <code>/tmp</code> is the only writable path in Lambda</li>
<li>during deployment, compile PHP's opcache to e.g. <code>/bref/opcache</code></li>
<li>on cold start, copy <code>/bref/opcache</code> to <code>/tmp/opcache</code></li>
</ul><p>To clarify if you're not familiar with Lambda: <code>/tmp</code> will always be empty on cold starts (you can't add files there via your container image), and is not shared between Lambda invocations.</p><p>This approach wasn't working well. Copying megabytes of data on startup was slower than deploying without a pre-warmed opcache. PHP is just that fast, and it's hard to complain about it :p</p><p>I then tried something else: if PHP could read the opcache as read-only from <code>/bref/opcache</code> directly that would be great. After all, we don't need PHP to <em>write</em> to that directory after startup since it will all be kept in memory. But PHP doesn't support that: if a directory is configured for opcache, PHP expects to use it.</p><p>But it turns out I wasn't the only one wanting that: <a href="https://github.com/iamacarpet">@iamacarpet</a> opened an issue in the PHP repository and advocated that exact use case, not specifically for Lambda but also for all container-based deployments where the filesystem is read-only.</p><p>He ended up working on <a href="https://github.com/php/php-src/pull/16551">a pull request that was accepted and merged</a> in… PHP 8.5!</p><p>I upgraded my test project to PHP 8.5 (in beta at the time of writing) and Bref v3 (in alpha at the time of writing), and used the new <code>opcache.file_cache_read_only=1</code> option. It worked!</p><blockquote>
<p>4. you need a way to trigger opcache compilation to disk</p>
</blockquote><p>The last step is to actually compile some PHP files to opcache files during deployment. Here is an example script:</p><pre class="language-php hljs php" data-lang="php">&lt;?php
$dirs = ['bootstrap', 'app', 'storage/framework/views', 'vendor/composer', 'vendor/bref', 'vendor/symfony/http-foundation', 'vendor/symfony/http-kernel', 'vendor/psr'];
foreach ($dirs as $dir) {
    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS));
    foreach ($it as $f) {
        if ($f-&gt;getExtension() === 'php') {
            $filename = $f-&gt;getPathname();
            $result = opcache_compile_file($filename);
            if ($result === false) {
                echo "[opcache] Failed to compile " . $filename . "\n";
                exit(1);
            }
            // Check the file is compiled
            if (!opcache_is_script_cached($filename)) {
                echo "[opcache] File not cached " . $filename . "\n";
            }
        }
    }
}
</pre><p>This is a rough script (yes, it’s ugly), feel free to make it more robust and nicer.</p><p>One thing I noticed is that some files would <strong>not</strong> compile, but no error was thrown. Hence the second check with <code>if (!opcache_is_script_cached($filename))</code>. I could not explain why some files would not be compiled, and it even seemed a bit random. Anyway, I didn't need <em>exactly</em> all files to be compiled so I ignored this for my tests and moved on.</p><p>Putting all this together, here is a Dockerfile:</p><pre class="language-dockerfile hljs dockerfile" data-lang="dockerfile"># syntax=docker/dockerfile:1.7
FROM bref/php-84:3
COPY --link . /var/task
RUN mkdir -p /bref/opcache
# Clear any cached config that might reference local paths
RUN php artisan config:clear &amp;&amp; php artisan route:clear &amp;&amp; php artisan view:clear
RUN php artisan optimize
# The views are compiled in /tmp/storage/framework/views, let's copy them to `storage/framework/views` so that they are persisted
RUN mkdir -p storage/framework/views \
    &amp;&amp; cp -r /tmp/storage/framework/views/* storage/framework/views/ \
    &amp;&amp; rm -rf /tmp/*
# `opcache.file_cache_read_only=0` so that opcache actually writes the opcache files
RUN php -d opcache.file_cache_read_only=0 compile-opcache.php
RUN chmod -R 777 storage/framework/views
RUN chmod -R 777 bootstrap/cache
RUN chmod -R 755 /bref/opcache
</pre><p>And here is the <code>php.ini</code> file I used (both locally and in Lambda):</p><pre class="language-ini hljs ini" data-lang="ini">opcache.file_cache=/bref/opcache
opcache.file_cache_read_only=1
opcache.file_cache_only=0
opcache.use_cwd=0
opcache.validate_timestamps=0
opcache.file_cache_consistency_checks=0
</pre><h2>Results</h2><p>All this for what? <strong>Cold starts were 40% faster</strong>. This is compared to zip deployments without any cache pre-built.</p><p>I would have loved an even more drastic improvement, but it is still significant. Overall, the improvement can be attributed to:</p><ul><li>switching from zip to container deployments (which has better cold starts in itself)</li>
<li>compiling Laravel's config, routes, etc. before deploying</li>
<li>compiling opcache before deploying</li>
</ul><p>But Lambda cold starts still need to mount the application and start PHP-FPM. Unless AWS Lambda SnapStart becomes available for custom runtimes, we shouldn’t expect drastic improvements.</p><p>I enjoyed digging <em>very</em> deep into this problem, especially as I'm working intensely on Bref v3 and benchmarking so many things to improve performance overall. But I want to reframe the problem: I don't think it's actually one in most situations.</p><p><strong>Cold starts are very rare. About 0.3% of all invocations.</strong></p><p>We tend to see them as developers because when we deploy we are the first to invoke the app after deployment. Most end users don't notice them, especially compared to things like 200ms network latency when visiting a site hosted on the other side of the planet. So I think it's important to put things into perspective and not conflate "working on fun deep technical challenges" with "this must be a very important problem".</p><p>And if you do want to play with these ideas and try benchmarking on your own, a few notes for you:</p><ul><li>be very careful how you measure cold starts, it's easy to mess things up (see below)</li>
<li>never measure averages in metrics (especially those with high variability where random extremes skew averages), use percentiles</li>
<li>learn how containers start up on Lambda: AWS does a lot of optimizations on the image on the very first invocation. You may see latencies of 5 or even 8 seconds, these will mess up your metrics for no reason (end users would never see this cold start in reality). Instead "warm" the regional AWS Lambda caches for container images.</li>
<li>don't measure one or two cold starts, you need at least 20 for significant results</li>
</ul><p>As a bonus, here's a CloudWatch Logs Insight query you might want to use:</p><pre class="language-livecodeserver hljs livecodeserver" data-lang="livecodeserver">filter @type = "REPORT"
| stats
count(@type) as count,
min(@billedDuration) as min,
avg(@billedDuration) as avg,
pct(@billedDuration, 50) as p50,
max(@billedDuration) as max
by @log, (@initDuration &gt; 0) as coldstart
| sort @log
</pre>]]></summary>
    <link href="https://mnapoli.fr/optimizing-laravel-aws-lambda/"/>
    <updated>2025-09-08T14:32:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/fixing-inertia-error-handling/</id>
    <title><![CDATA[Fixing error handling in Inertia.js]]></title>
    <summary><![CDATA[<p><a href="https://bref.sh/cloud">Bref Cloud</a> is built with Laravel and <a href="https://inertiajs.com/">Inertia.js</a>. Inertia.js connects Laravel with Vue.js, making everything related to routing, auth, state management, and more, much easier to handle. <strong>Inertia.js is awesome</strong>.</p><p>However, there is one thing that I really dislike about it: how it handles server errors.</p><p><img src="https://mnapoli.fr/images/posts/inertia-error.gif" alt="" /></p><p>When an error occurs on the server, <strong>Inertia will show the error page in a modal</strong>. That feels very weird. Even weirded when that modal shows because an async request failed.</p><p>The official docs suggest to customize the error page, but I want to get rid of the modal entirely.</p><p><strong>Let's replace the modal with toast notifications.</strong></p><p>I'm using <a href="https://github.com/Maronato/vue-toastification">vue-toastification</a> for toast notifications. You can use any other library, but the idea is the same.</p><p>First, let's override how Laravel turns errors into HTTP responses in <code>bootstrap/app.php</code>. When we are in an Inertia request, instead of returning the error page, we will return a JSON response with the error message.</p><pre class="language-php hljs php" data-lang="php">return Application::configure(basePath: dirname(__DIR__))
    // ...
    -&gt;withExceptions(function (Exceptions $exceptions) {
        $exceptions-&gt;respond(function (Response $response, Throwable $exception, Request $request) {
            $isServerError = in_array($response-&gt;getStatusCode(), [500, 503], true);
            $isInertia = $request-&gt;headers-&gt;get('X-Inertia') === 'true';
            // When in an Inertia request, we don't want to show the default error modal
            if ($isServerError &amp;&amp; $isInertia) {
                $errorMessage = 'An internal error occurred, please try again. If the problem persists, please contact support.';
                // In local environment let's show the actual exception class &amp; message
                if (app()-&gt;isLocal()) {
                    $errorMessage .= sprintf("\n%s: %s", get_class($exception), $exception-&gt;getMessage());
                }
                return response()-&gt;json([
                    'error_message' =&gt; $errorMessage,
                ], $response-&gt;getStatusCode());
            }
            if ($response-&gt;getStatusCode() === 419) {
                return back()-&gt;with([
                    'flash.banner' =&gt; 'The page expired, please try again.',
                ]);
            }
            return $response;
        });
    })-&gt;create();
</pre><p>Now that Laravel returns a custom JSON response, we need to handle it in Inertia. Let's add this to the <code>app.js</code> file:</p><pre class="language-javascript hljs javascript" data-lang="javascript">import { router } from '@inertiajs/vue3'
router.on('invalid', (event) =&gt; {
    const responseBody = event.detail.response?.data;
    if (responseBody?.error_message) {
        const toast = useToast()
        toast.error(responseBody.error_message);
        event.preventDefault();
    }
});
// ...
</pre><p>In short, if the server returns a JSON response with an <code>error_message</code> key, we will show it in a toast notification. Otherwise, we will let Inertia.js handle the error as usual.</p><p><img src="https://mnapoli.fr/images/posts/inertia-error-2.png" alt="" /></p><p>Much better!</p>]]></summary>
    <link href="https://mnapoli.fr/fixing-inertia-error-handling/"/>
    <updated>2025-03-31T12:32:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/ffmpeg-with-php-on-aws-lambda/</id>
    <title><![CDATA[Using FFmpeg with PHP on AWS Lambda]]></title>
    <summary><![CDATA[<p>You can use FFmpeg with PHP on AWS Lambda. Here is how to do it with <a href="https://bref.sh/">Bref</a>.</p><h2>Switching to deploying containers on AWS Lambda</h2><p>The default approach to deploying PHP to AWS Lambda with Bref is to deploy via zip files, while the PHP runtime is provided by Bref via AWS Lambda layers. This is great because deployments are fast and simple. However, adding custom binaries to the Lambda environment is hard because you have to create your own "layers".</p><p>To add FFmpeg to Lambda, we can switch to deploying a container image to Lambda. This way we have all the familiar tools to install FFmpeg in the container image later on.</p><p>There is a <a href="https://bref.sh/docs/deploy/docker">Bref guide</a> on how to deploy a custom container image to Lambda, but here's the short version:</p><ol><li>we need a <code>Dockerfile</code>:</li>
</ol><pre class="language-bash hljs bash" data-lang="bash">FROM bref/php-82-fpm:2
# Copy our source code in the image
COPY . /var/task
# Configure the handler file (the entrypoint that receives all HTTP requests)
CMD ["public/index.php"]
</pre><ol start="2"><li>We change <code>serverless.yml</code> to deploy our container image:</li>
</ol><pre class="language-yaml hljs yaml" data-lang="yaml">service: myapp
provider:
    name: aws
    ecr:
        images:
            myimage:
                # Path to the `Dockerfile` file
                path: ./
functions:
    website:
        image:
            name: myimage
        events:
            - httpApi: '*'
</pre><p>Deploying is the same command as before: <code>serverless deploy</code>.</p><h2>Installing FFmpeg in the container image</h2><p>Now that we have a container image, we can install FFmpeg in it using <a href="https://docs.docker.com/build/building/multi-stage/">multi-stage builds</a>:</p><pre class="language-bash hljs bash" data-lang="bash"># This is Bref's "build" image that we can use to build custom binaries and extensions
FROM bref/build-php-82:2 as build
# Install ffmpeg
RUN set -xe; \
    mkdir -p /tmp/ffmpeg; \
    curl -Ls https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz \
    | tar xJC /tmp/ffmpeg --strip-components=1
RUN mv /tmp/ffmpeg/ffmpeg /opt/bin/ffmpeg
FROM bref/php-82-fpm:2
# Copy what we built above in our final image
COPY --from=build /opt /opt
# Copy our source code in the image
COPY . /var/task
# Configure the handler file (the entrypoint that receives all HTTP requests)
CMD ["public/index.php"]
</pre><p>The <code>ffmpeg</code> binary is now available in the <code>$PATH</code> of the Lambda environment, PHP can use it like any other binary:</p><pre class="language-php hljs php" data-lang="php">exec('ffmpeg -i input.mp4 output.mp4');
</pre>]]></summary>
    <link href="https://mnapoli.fr/ffmpeg-with-php-on-aws-lambda/"/>
    <updated>2024-10-03T20:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://planetscale.com/blog/using-planetscale-with-serverless-framework-node-apps-on-aws</id>
    <title><![CDATA[Using PlanetScale with Serverless Framework Node applications on AWS]]></title>
    <summary><![CDATA[<p>The <a href="https://github.com/serverless/serverless">Serverless Framework</a> is great for building Node applications on AWS Lambda. The only thing missing is a serverless database. In this article, we will explore how to use PlanetScale as the database for a serverless Node application.</p><p>Before deploying to AWS Lambda, you will need:</p><ul><li>an AWS account (to create one, go to <a href="https://aws.amazon.com/">aws.amazon.com</a> and click <em>Sign up</em>),</li>
<li>the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI installed on your computer.</li>
</ul><blockquote>
<p>Note: while the example of this article should stay under the <a href="https://aws.amazon.com/free/">AWS free tier</a> (1 million free AWS Lambda invocations per month), be advised that building on AWS can incur costs.</p>
</blockquote><p>You can install the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI using NPM:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>npm install-gserverless</pre></div><p>If you don't have NPM or want to learn more, <a href="https://www.serverless.com/framework/docs/getting-started">read the Serverless documentation</a>.</p><p>Now connect the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI to your AWS account via AWS access keys. Create AWS access keys <a href="https://www.serverless.com/framework/docs/providers/aws/guide/credentials#creating-aws-access-keys">by following this guide</a>, then set them up on your computer using the following command:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless configcredentials--provideraws--key&lt;key&gt;--secret&lt;secret&gt;</pre></div><p>Serverless Framework is a CLI tool that helps us create and deploy serverless applications. Its configuration is stored in a <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless.yml</code> file, which describes what will be deployed to AWS.</p><p>To deploy a Node application, we can create a simple <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless.yml</code> file:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>YAML</p><pre>service:demo# name of the application
provider:
name:aws
runtime:nodejs18.x
region:us-east-1
functions:
api:
handler:index.handler
url:true</pre></div><p>In the configuration above, we define a single AWS Lambda function called <code class="break-words lg:whitespace-nowrap lg:break-normal">api</code>, running NodeJS 18, with a public URL.</p><p>Our API handler will be a <code class="break-words lg:whitespace-nowrap lg:break-normal">handler()</code> function returned by <code class="break-words lg:whitespace-nowrap lg:break-normal">index.js</code> (learn more about handlers <a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html">in the AWS Lambda documentation</a>). Let's create the <code class="break-words lg:whitespace-nowrap lg:break-normal">index.js</code> file:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Javascript</p><pre>exportasyncfunctionhandler(event) {
return {
    hello:'world'
  }
}</pre></div><p>Note that we will be using ESM features (like <code class="break-words lg:whitespace-nowrap lg:break-normal">export</code> and <code class="break-words lg:whitespace-nowrap lg:break-normal">import</code>), so let's create a <code class="break-words lg:whitespace-nowrap lg:break-normal">package.json</code> file with <code class="break-words lg:whitespace-nowrap lg:break-normal">"type": "module"</code>:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>JSON</p><pre>{
"type":"module"
}</pre></div><p>Our simple Node example is ready to be deployed with <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless deploy</code>, but let's add PlanetScale into the mix first!</p><p>In your PlanetScale account, start by creating a database in the same region as the AWS application (<code class="break-words lg:whitespace-nowrap lg:break-normal">us-east-1</code> in our example). Then, click the <strong>Connect</strong> button and select "Connect with: @planetscale/database". That will let us retrieve the database username and password.</p><p>To connect to the database in our code, we will use the <a href="https://planetscale.com/docs/tutorials/planetscale-serverless-driver">PlanetScale serverless driver</a>. Let's install it with NPM:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>npm install@planetscale/database</pre></div><p>Now that the driver is installed, we can connect to our PlanetScale database with the <code class="break-words lg:whitespace-nowrap lg:break-normal">connect()</code> function:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Javascript</p><pre>import { connect } from'@planetscale/database'
constconn=connect({
// With the serverless driver, the host is always 'aws.connect.psdb.cloud'
  host:'aws.connect.psdb.cloud',
  username:'&lt;user&gt;',
  password:'&lt;password&gt;'
})
exportasyncfunctionhandler(event) {
constresult=awaitconn.execute('SELECT * FROM records')
returnresult.rows
}</pre></div><p>Note the following details:</p><ul><li>We are connecting to the database <strong>outside</strong> the <code class="break-words lg:whitespace-nowrap lg:break-normal">handler()</code> function. This is to reuse the same connection for all HTTP requests. If we were to connect inside the <code class="break-words lg:whitespace-nowrap lg:break-normal">handler()</code> function, a new connection would be created for each request, which would be inefficient.</li>
<li>We are querying the <code class="break-words lg:whitespace-nowrap lg:break-normal">records</code> table. This table doesn't exist yet, we will create it below.</li>
<li>We don't want to store the database credentials in the code. We will use environment variables instead.</li>
</ul><p>Let's update our code to use environment variables. For the sake of the example, we will also create the <code class="break-words lg:whitespace-nowrap lg:break-normal">records</code> table on the fly:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Javascript</p><pre>import { connect } from'@planetscale/database'
constconn=connect({
// With the serverless driver, the host is always the same
  host:'aws.connect.psdb.cloud',
  username:process.env.DATABASE_USERNAME,
  password:process.env.DATABASE_PASSWORD
})
// Create the table if it doesn't exist (just for demo purposes)
// In a real application, we would run database migrations outside the function
awaitconn.execute('CREATE TABLE IF NOT EXISTS records (id INT PRIMARY KEY auto_increment, name VARCHAR(255))')
exportasyncfunctionhandler(event) {
// Insert a new record
constqueryParameter=event.queryStringParameters?.name ??'test'
awaitconn.execute('INSERT INTO records (name) VALUES (?)', [queryParameter])
// Retrieve all records
constresult=awaitconn.execute('SELECT * FROM records')
returnresult.rows
}</pre></div><p>We now need to set the <code class="break-words lg:whitespace-nowrap lg:break-normal">DATABASE_USERNAME</code> and <code class="break-words lg:whitespace-nowrap lg:break-normal">DATABASE_PASSWORD</code> environment variables. We can define them in <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless.yml</code> and use AWS SSM to store the database password securely:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>YAML</p><pre>provider:
name:aws
runtime:nodejs18.x
region:us-east-1
environment:
DATABASE_USERNAME:&lt;username-here&gt;
DATABASE_PASSWORD:${ssm:/planetscale/db-password}</pre></div><p>The database password will be stored in <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS SSM</a> (at no extra cost) so it is not visible in the code. The <code class="break-words lg:whitespace-nowrap lg:break-normal">${ssm:/planetscale/db-password}</code> variable will retrieve the value from SSM on deployment. The SSM parameter can be created with the AWS CLI via the following command:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>aws ssmput-parameter--regionus-east-1--name'/planetscale/db-password'--typeSecureString--value'replace-me'
# replace the `replace-me` string with the database password!</pre></div><p>If you don't use the AWS CLI, you can also create the parameter <a href="https://us-east-1.console.aws.amazon.com/systems-manager/parameters/aws/create?region=us-east-1">in the AWS Console</a>:</p><p><img alt="Create SSM parameter" src="https://planetscale.com/images/blog/content/serverless-framework-node-apps-aws/ssm.png" data-nimg="fill" class="image !relative m-auto h-auto max-w-full rounded-xs object-contain c4" /></p><p><strong>Our application is now ready!</strong> Let's deploy it:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless deploy</pre></div><p>When finished, the <code class="break-words lg:whitespace-nowrap lg:break-normal">deploy</code> command will display the URL of our Node application. The URL should look like this: <code class="break-words lg:whitespace-nowrap lg:break-normal">https://&lt;id&gt;.lambda-url.us-east-1.on.aws/</code>. We can open it in the browser or request it with <code class="break-words lg:whitespace-nowrap lg:break-normal">curl</code>:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>curl https://&lt;id&gt;.lambda-url.us-east-1.on.aws/</pre></div><p>The response should list the records in the <code class="break-words lg:whitespace-nowrap lg:break-normal">records</code> table. A new record will be created every time the URL is requested. We can also provide a <code class="break-words lg:whitespace-nowrap lg:break-normal">name</code> parameter to change the name of the record inserted in the database:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>curl https://&lt;id&gt;.lambda-url.us-east-1.on.aws/?name=hello</pre></div><p>Besides the incredible scalability provided by the combination of AWS Lambda and PlanetScale, another benefit we get from this setup is the ability to combine <strong>Serverless Framework stages</strong> and <strong><a href="https://planetscale.com/docs/concepts/branching#development-and-production-branches">PlanetScale branches</a></strong>.</p><p>We could imagine, for example, a <code class="break-words lg:whitespace-nowrap lg:break-normal">dev</code> stage for development and a <code class="break-words lg:whitespace-nowrap lg:break-normal">prod</code> stage for production. The <code class="break-words lg:whitespace-nowrap lg:break-normal">dev</code> stage would use a <code class="break-words lg:whitespace-nowrap lg:break-normal">development</code> branch in PlanetScale, while the <code class="break-words lg:whitespace-nowrap lg:break-normal">prod</code> stage would use the <code class="break-words lg:whitespace-nowrap lg:break-normal">main</code> production branch.</p><p>Using <a href="https://www.serverless.com/framework/docs/guides/parameters#stage-parameters">stage parameters</a>, we can set different credentials to use to connect to PlanetScale depending on the stage:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>YAML</p><pre>provider:
name:aws
runtime:nodejs18.x
region:us-east-1
environment:
DATABASE_USERNAME:${param:dbUser}
DATABASE_PASSWORD:${param:dbPassword}
params:
dev:
dbUser:&lt;dev-username-here&gt;
dbPassword:${ssm:/planetscale/dev/db-password}
prod:
dbUser:&lt;prod-username-here&gt;
dbPassword:${ssm:/planetscale/prod/db-password}</pre></div><p>When deploying, we can specify the stage to deploy via the <code class="break-words lg:whitespace-nowrap lg:break-normal">--stage</code> option:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless deploy--stagedev
serverless deploy--stageprod</pre></div><p>Each stage (dev and prod) will result in entirely separate infrastructures on AWS, and each one will use its own PlanetScale branch.</p><p>That setup makes it easy to test code changes and database schema changes in a development environment that is identical to and isolated from the production environment. Once approved, schema changes can be applied to the production branch with a <a href="https://planetscale.com/docs/concepts/branching#how-to-make-schema-changes-on-a-branch-with-safe-migrations-enabled">PlanetScale deploy request</a>, and code changes can be deployed to production via the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless deploy</code> command.</p><p>In this article, we learned how to integrate PlanetScale with Node applications built using the Serverless Framework on AWS. This gives us a completely serverless stack with extreme scalability yet simple to set up and maintain.</p><p>Now that we have a basic application running we can explore more complex topics, such as:</p><ul><li><a href="https://www.serverless.com/framework/docs/providers/aws/events/http-api">Creating multiple HTTP routes</a></li>
<li><a href="https://www.serverless.com/framework/docs/providers/aws/guide/workflow">Setting up a complete deployment workflow</a> for the Node application</li>
<li><a href="https://planetscale.com/docs/concepts/planetscale-workflow">Dive into the PlanetScale workflow</a> for branching databases, non-blocking schema changes, and more</li>
</ul><p>Feel free to explore the <a href="https://planetscale.com/docs">PlanetScale documentation</a> as well as the <a href="https://www.serverless.com/framework/docs">Serverless Framework documentation</a> to learn more.</p>]]></summary>
    <link href="https://planetscale.com/blog/using-planetscale-with-serverless-framework-node-apps-on-aws"/>
    <updated>2023-06-13T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://planetscale.com/blog/serverless-laravel-app-aws-lambda-bref-planetscale</id>
    <title><![CDATA[Serverless Laravel applications with AWS Lambda and PlanetScale]]></title>
    <summary><![CDATA[<p>In general, PHP-based applications, like Laravel, are deployed on servers. It is also possible to <a href="https://bref.sh/docs/#what-is-bref">run them serverless</a> — for example, on AWS Lambda.</p><p>This approach provides several benefits:</p><ul><li>Instant and effortless autoscaling to handle incoming traffic.</li>
<li>Redundant and resilient infrastructure out of the box, without extra complexity.</li>
<li>Pay-per-request billing.</li>
</ul><p>PlanetScale is a great database to pair with serverless Laravel applications running on Lambda. In this article, we will create a new Laravel application, run it on AWS Lambda using <a href="https://bref.sh/">Bref</a>, connect to a PlanetScale MySQL database, and do a load test to look at the performance.</p><p>Let's start from scratch and create a new Laravel project with <a href="https://getcomposer.org/">Composer</a>:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>composer create-projectlaravel/laravelexample-app
cdexample-app</pre></div><p>We can run our application locally with <code class="break-words lg:whitespace-nowrap lg:break-normal">php artisan serve</code>, but let's run it in the cloud on AWS Lambda instead.</p><p>To deploy Laravel to AWS Lambda, we can use <a href="https://bref.sh/">Bref</a>. Bref is an open-source project that provides support for PHP on AWS Lambda. It also provides a <a href="https://bref.sh/docs/frameworks/laravel.html">Laravel integration</a> that simplifies configuring Laravel for Lambda.</p><h3>Prerequisites</h3><p>Before deploying to AWS Lambda, you will need:</p><ul><li>An AWS account (to create one, go to <a href="https://aws.amazon.com/">aws.amazon.com</a> and click "<em>Sign up</em>").</li>
<li>The <a href="https://github.com/serverless/serverless"><code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI</a> installed on your computer.</li>
</ul><p>You can install the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI using NPM:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>npm install-gserverless</pre></div><p>If you don't have NPM or want to learn more, <a href="https://www.serverless.com/framework/docs/getting-started">read the Serverless documentation</a>.</p><p>Now, connect the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless</code> CLI to your AWS account via AWS access keys. Create AWS access keys <a href="https://bref.sh/docs/installation/aws-keys.html">by following the guide</a>, and then set them up on your computer using the following command:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless configcredentials--provideraws--key&lt;key&gt;--secret&lt;secret&gt;</pre></div><p>Now that everything is ready, let's install Bref and its Laravel integration:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>composer requirebref/brefbref/laravel-bridge--update-with-dependencies</pre></div><p>Then, let's create a <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless.yml</code> configuration file:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>php artisanvendor:publish--tag=serverless-config</pre></div><p>This configuration file describes what will be deployed to AWS. Let's deploy now:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless deploy</pre></div><p>When finished, the <code class="break-words lg:whitespace-nowrap lg:break-normal">deploy</code> command will display the URL of our Laravel application.</p><p>Now that Laravel is running in the cloud, let's set it up with a PlanetScale database. Start in PlanetScale by creating a new database in the same region as the AWS application (<code class="break-words lg:whitespace-nowrap lg:break-normal">us-east-1</code> by default).</p><p>Click the <strong>Connect</strong> button and select "Connect with: PHP (PDO)". That will let us retrieve the host, database name, user, and password.</p><p>Edit the <code class="break-words lg:whitespace-nowrap lg:break-normal">.env</code> configuration file to set up the database connection:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>DB_CONNECTION=mysql
DB_HOST=&lt;host url&gt;
DB_PORT=3306
DB_DATABASE=&lt;database_name&gt;
DB_USERNAME=&lt;user&gt;
DB_PASSWORD=&lt;password&gt;
MYSQL_ATTR_SSL_CA=/opt/bref/ssl/cert.pem</pre></div><p>Don't skip the <code class="break-words lg:whitespace-nowrap lg:break-normal">MYSQL_ATTR_SSL_CA</code> line: SSL certificates are required to <a href="https://planetscale.com/docs/concepts/secure-connections">secure the connection between Laravel and PlanetScale</a>. Note that the path in AWS Lambda (<code class="break-words lg:whitespace-nowrap lg:break-normal">/opt/bref/ssl/cert.pem</code>) differs from the one on your machine (likely <code class="break-words lg:whitespace-nowrap lg:break-normal">/etc/ssl/cert.pem</code>). If you run the application locally, you will need to change this environment variable back and forth.</p><p>Next, redeploy the application:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless deploy</pre></div><p>Now that Laravel is configured, we can run database migrations to set up DB tables. To do so, we can run Laravel Artisan commands <strong>in AWS Lambda</strong> using the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless bref:cli</code> command:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless bref:cli--args="migrate --force"</pre></div><p>That's it! Our database is ready to use.</p><p>To test the database connection, let's create sample data in the <code class="break-words lg:whitespace-nowrap lg:break-normal">users</code> table created out of the box by Laravel.</p><p>Edit the <code class="break-words lg:whitespace-nowrap lg:break-normal">database/seeders/DatabaseSeeder.php</code> class and uncomment the following line so that we can seed our database with 10 fake users:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>PHP</p><pre>\App\Models\User::factory(10)-&gt;create();</pre></div><p>Now, let's create a public API route that returns all the users from the database. Add the following code to <code class="break-words lg:whitespace-nowrap lg:break-normal">routes/api.php</code>:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>PHP</p><pre>Route::get('/users',function () {
return\App\Models\User::all();
});</pre></div><p>Let's deploy these changes:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless deploy</pre></div><p>Now, let's seed the database with 10 fake users:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>serverless bref:cli--args="migrate:fresh --seed --force"</pre></div><p>We can now retrieve our 10 users via the API route we created:</p><div class="mt-2 mb-4 max-w-full rounded border"><pre>curl https://&lt;application url&gt;/api/users
</pre></div><p>The execution model of AWS Lambda gives us instant autoscaling without any configuration. To illustrate that, I have performed a simple load test against the application we deployed above, using <a href="https://planetscale.com/pricing">PlanetScale's free tier database</a>.</p><p>The only change I made is to disable Laravel's default rate limiting for API calls (<code class="break-words lg:whitespace-nowrap lg:break-normal">ThrottleRequests</code> middleware) in <code class="break-words lg:whitespace-nowrap lg:break-normal">app/Http/Kernel.php</code> because it would get in the way of my load test.</p><p>Furthermore, I did not ramp up traffic progressively because I wanted to show Lambda's instant scalability. I used <code class="break-words lg:whitespace-nowrap lg:break-normal">ab</code> (<a href="https://httpd.apache.org/docs/2.4/programs/ab.html">Apache's benchmarking tool</a>) to request the <code class="break-words lg:whitespace-nowrap lg:break-normal">/api/users</code> endpoint with 50 threads (50 HTTP requests made in parallel continuously):</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><p>Terminal</p><pre>ab -c50-n10000https://&lt;my-api-url&gt;/api/users</pre></div><p>When looking at the AWS Lambda and API Gateway metrics, we see the following numbers:</p><ul><li>Laravel scaled instantly from zero to <strong>3,800 HTTP requests/minute</strong>.</li>
<li>100% of HTTP requests were handled successfully.</li>
<li>The median PHP execution time (p50) for each HTTP request is <strong>75ms</strong>.</li>
<li>95% of requests (p95) are processed in less than 130ms.</li>
<li>PlanetScale processed up to 180 queries/s.</li>
<li>The median PlanetScale query execution time is <strong>0.3ms</strong>.</li>
</ul><p><img alt="Load test - 50 requests in parallel" srcset="/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=256&amp;q=75 256w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=384&amp;q=75 384w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=768&amp;q=75 768w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=1024&amp;q=75 1024w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=1280&amp;q=75 1280w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=1536&amp;q=75 1536w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=3840&amp;q=75 3840w" src="https://planetscale.com/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-1.png&amp;w=3840&amp;q=75" data-nimg="fill" class="image !relative m-auto h-auto max-w-full rounded-xs object-contain c4" /></p><p>The load test was performed against a freshly deployed application. That means the first requests were <em>cold starts</em>: New AWS Lambda instances started and scaled up to handle the incoming traffic. The cold starts usually have a much slower execution time (one second instead of 75ms). However, we do not see them in the p50 or p95 metrics because they only impacted 1% of the requests in the first minute. After the first 50 requests (cold starts), all the other requests were warm invocations.</p><p>Note that we are looking at the AWS Lambda duration instead of HTTP response time: This is to exclude any latency related to networking (and thus have reproducible and comparable results). This is not the HTTP response time real users would see as, like on any server, the network adds latency to HTTP responses.</p><p>After a few minutes, I dropped the traffic from 50 requests in parallel to one. The PHP execution time stayed identical. This illustrates that the load did not impact the response time.</p><p><img alt="Second load test - 1 request" srcset="/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=256&amp;q=75 256w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=384&amp;q=75 384w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=768&amp;q=75 768w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=1024&amp;q=75 1024w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=1280&amp;q=75 1280w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=1536&amp;q=75 1536w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=3840&amp;q=75 3840w" src="https://planetscale.com/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-2.png&amp;w=3840&amp;q=75" data-nimg="fill" class="image !relative m-auto h-auto max-w-full rounded-xs object-contain c4" /></p><h3>Improving performance to speed up the SSL connection</h3><p>For many web applications, responding in about 100ms is more than satisfactory. However, some use cases may require lower latency.</p><p>Since Laravel connects to PlanetScale over SSL, creating the SSL connection can take longer than running the SQL query itself. PlanetScale itself can easily <a href="https://planetscale.com/blog/one-million-connections">handle unlimited connections</a> using built-in connection pooling, which massively improves performance by keeping those database connections open between requests.</p><p>However, PHP, by design, shares nothing across requests. This means at the end of every request, PHP will close the connection to the database.</p><p>To circumvent this problem, we can use Laravel Octane to gain performance in two ways:</p><ul><li>Keeping the Laravel application in memory across requests <a href="https://bref.sh/docs/frameworks/laravel.html#laravel-octane">using Laravel Octane</a>.</li>
<li>Reusing SQL connections across requests (instead of reconnecting every time).</li>
</ul><p>Bref supports Laravel Octane natively. We need to change the <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless.yml</code> configuration to enable it. Change the <code class="break-words lg:whitespace-nowrap lg:break-normal">web</code> function configuration to this:</p><div class="mt-2 mb-4 max-w-full rounded border" data-rehype-pretty-code-fragment=""><pre>web:
  handler: Bref\LaravelBridge\Http\OctaneHandler
  runtime: php-81
  environment:
    BREF_LOOP_MAX: 250
    OCTANE_PERSIST_DATABASE_SESSIONS: 1
  events:
    - httpApi: '*'</pre></div><p>Let's redeploy with <code class="break-words lg:whitespace-nowrap lg:break-normal">serverless deploy</code> and run the load test again:</p><p><img alt="Running the load test again" srcset="/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=256&amp;q=75 256w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=384&amp;q=75 384w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=768&amp;q=75 768w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=1024&amp;q=75 1024w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=1280&amp;q=75 1280w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=1536&amp;q=75 1536w, /_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=3840&amp;q=75 3840w" src="https://planetscale.com/_next/image?url=%2Fimages%2Fblog%2Fcontent%2Flaravel-bref-planetscale%2Fload-test-3.png&amp;w=3840&amp;q=75" data-nimg="fill" class="image !relative m-auto h-auto max-w-full rounded-xs object-contain c4" /></p><p>We notice the following improvements:</p><ul><li>The median PHP execution time (p50) went from 75ms to <strong>14ms</strong>.</li>
<li>95% of requests (p95) are processed in less than <strong>35ms</strong>.</li>
<li>Laravel handled 1,000 more requests/minute, though this number is not important: We could simply send more requests in our load test to reach a higher number anytime.</li>
</ul><p>Here are some next steps:</p><ul><li><a href="https://github.com/brefphp/examples/tree/master/Laravel/planetscale">Download and run</a> the code used in this blog post.</li>
<li>Learn more about using PlanetScale with Bref: <a href="https://bref.sh/docs/environment/database-planetscale.html">MySQL compatibility, data imports, and schema changes workflow</a>.</li>
<li>Learn more about running Laravel on AWS Lambda: <a href="https://bref.sh/docs/frameworks/laravel.html">running Artisan commands, setting up assets, queues, and more</a>.</li>
</ul><p>You can also <a href="https://planetscale.com/docs">learn more about PlanetScale</a> and <a href="https://bref.sh/docs/">AWS Lambda with Bref</a> in the respective documentation.</p>]]></summary>
    <link href="https://planetscale.com/blog/serverless-laravel-app-aws-lambda-bref-planetscale"/>
    <updated>2023-05-03T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://bref.sh/docs/news/02-bref-2.0.html</id>
    <title><![CDATA[Bref 2.0 is released ?]]></title>
    <summary><![CDATA[<p>The work on what would be Bref 2.0 started in October 2021, about 1.5 year ago. We went through many different strategies, experiments, rewrites, over <strong>700 commits</strong> to finally land with the stable release.</p><p>So far, Bref has been installed more than 2 million times and powers more than <strong>10 billion Lambda executions</strong> (aka requests) every month<a href="https://bref.sh/docs/runtimes/#bref-ping">*</a>.</p><p>That's <a href="https://twitter.com/matthieunapoli/status/1603032544424894464">1 in every 1000 AWS Lambda executions</a>!</p><p><img src="https://bref.sh/docs/news/02/executions.png" alt="" /></p><p>Today, we celebrate these achievements, the ongoing work, and <strong>the release of Bref 2.0</strong> ?</p><p>Let's check out what's new in v2.</p><h2 id="bref-20">Bref 2.0</h2><p>Here's a summary, we'll dive in the details below:</p><ul><li>Simpler <code>serverless.yml</code> configuration for setting up PHP.</li>
<li>Revamped Laravel integration.</li>
<li>ARM/Graviton support (faster processors with lower Lambda costs).</li>
<li>Faster deployments by default.</li>
<li><code>vendor/bin/bref cli</code> becomes much simpler.</li>
<li>Automatically load secrets in environment variables at runtime.</li>
<li>Simpler <code>docker-compose.yml</code> for local development.</li>
<li>PHP constructs for AWS CDK support.</li>
<li>The internals (the scripts that build the runtime) have been rewritten at least 4 times (<a href="https://github.com/brefphp/aws-lambda-layers">just look at the number of commits on the v2 runtimes…</a>) but they are much better now: they are now tested, we understand all the code, we optimized their size, we've made the builds as fast as possible, and contributions and maintenance as easy as possible.</li>
</ul><p>What did we break? <strong>Nothing major</strong>, the upgrade should be smooth. Here are the details:</p><ul><li>PHP 8.0+ is now required (7.4 support is dropped).</li>
<li>Serverless Framework v3 is now required (2.x is obsolete). Run <code>serverless --version</code> to check.</li>
<li>The <code>vendor/bin/bref</code> commands have been moved to the <code>serverless</code> CLI (detailed below).</li>
<li>If you have a <code>docker-compose.yml</code> for local development, it needs to be adjusted (detailed below).</li>
<li>The <code>separateVendor</code> option in <code>serverless.yml</code> has been removed (unmaintained feature).</li>
</ul><p>Bref 2.0 lets us configure the runtime and PHP version in a much simpler way in <code>serverless.yml</code> (<a href="https://github.com/brefphp/bref/pull/1394">#1394</a>). Here's an example below.</p><p>Note: <strong>this new feature is optional</strong>, you can keep using the Bref v1 syntax as it still works (this is not a breaking change).</p><p>Before (Bref v1 syntax):</p><pre class="language-yaml">provider:
    name: aws
    runtime: provided.al2
functions:
    api:
        handler: public/index.php
        # ...
        layers:
            - ${bref:layer.php-81-fpm}</pre><p>After (Bref v2 syntax):</p><pre class="language-yaml">provider:
    name: aws
functions:
    api:
        handler: public/index.php
        # ...
        runtime: php-81-fpm</pre><p>As you can see, we no longer have to set <code>runtime: provided.al2</code> and add the Bref layers. We can now directly set a PHP runtime (<code>php-81</code>, <code>php-81-fpm</code>, <code>php-81-console</code>) and Bref will transform this into the proper runtime + layers configuration.</p><p>This works for all the Bref runtimes (<a href="https://bref.sh/docs/runtimes/http.html">FPM</a>, <a href="https://bref.sh/docs/runtimes/function.html">function</a> and <a href="https://bref.sh/docs/runtimes/console.html">console</a>) and all supported PHP versions (<code>80</code>, <code>81</code>, and <code>82</code> at the moment). Here's a recap:</p><pre class="language-yaml"># PHP-FPM runtime (web apps)
runtime: provided.al2
layers:
    - ${bref:layer.php-81-fpm}
# becomes:
runtime: php-81-fpm
# Function runtime
runtime: provided.al2
layers:
    - ${bref:layer.php-81}
# becomes:
runtime: php-81
# Console runtime
runtime: provided.al2
layers:
    - ${bref:layer.php-81}
    - ${bref:layer.console}
# becomes:
runtime: php-81-console</pre><p>The Bref documentation has been updated to reflect these changes.</p><h2 id="new-laravel-integration">New Laravel integration</h2><p>Bref provides the <a href="https://github.com/brefphp/laravel-bridge">Laravel bridge</a> to easily deploy Laravel applications to AWS Lambda.</p><p>However, that bridge was lagging behind and was limited in some features. The community (<a href="https://cachewerk.com/">CacheWerk</a>, <a href="https://github.com/tillkruss">Till Kruss</a>, and <a href="https://github.com/georgeboot">George Boot</a>) maintained a better alternative at <a href="https://github.com/cachewerk/bref-laravel-bridge">cachewerk/bref-laravel-bridge</a>.</p><p>With Bref 2.0, we are joining forces and their bridge will become the new Bref Laravel bridge (<a href="https://github.com/brefphp/laravel-bridge/pull/94">#94</a>)!</p><p>The package name will stay <code>bref/laravel-bridge</code>, but a new major version (2.0) has been published with these improvements:</p><ul><li>Laravel Octane support!</li>
<li>Automatic config caching (if not already cached) on Lambda cold start.</li>
<li>Maintenance mode.</li>
<li>Storage directory moved entirely to <code>/tmp</code>.</li>
<li>AWS credentials automatically set up for S3 disks, SQS queues, and DynamoDB caches.</li>
<li>and more minor changes, see <a href="https://github.com/brefphp/laravel-bridge/pull/94">#94</a>.</li>
</ul><p>More importantly, it <strong>improves how Laravel Queues are supported</strong> (this is the most significant breaking change):</p><ul><li>Laravel bridge 1.x adapted Laravel Queues to SQS: the retry strategy and storing of failed messages was handled by SQS (configured in <code>serverless.yml</code>). All SQS features were supported, but only a fraction of Laravel Queues features were supported.</li>
<li>Laravel bridge 2.0 follows the official behavior of Laravel Queues instead. Only a fraction of SQS features are supported, but <strong>all</strong> features of Laravel Queues are now supported (e.g. job retry, delay, rate limiting, storing failed messages…).</li>
</ul><p>That should make the experience for Laravel users much simpler, as existing projects can be ported to Lambda with no changes.</p><p><em>Note that it is possible to stay on the 1.x version of the Laravel bridge.</em></p><p>Let's take the opportunity to send huge thanks to Till and George for building such an excellent integration, and for joining the Bref organization on GitHub ?</p><p>If you want to get started with Laravel on Bref, <a href="https://bref.sh/docs/frameworks/laravel.html">check out the documentation</a>.</p><h2 id="armgraviton-support">ARM/Graviton support</h2><p>Since 2021, it is possible to deploy Lambda functions <a href="https://aws.amazon.com/blogs/aws/aws-lambda-functions-powered-by-aws-graviton2-processor-run-your-functions-on-arm-and-get-up-to-34-better-price-performance/">running on ARM processors</a> (called Graviton) instead of Intel x86 processors. However, Bref did not support that.</p><p>These processors usually run applications faster (<a href="https://twitter.com/matthieunapoli/status/1605583651659345921">example here</a>), and ARM functions <a href="https://aws.amazon.com/lambda/pricing/">cost 20% less</a>.</p><p>With Bref v2, we can deploy on ARM by setting the <code>architecture</code> field to <code>arm64</code>:</p><pre class="language-yaml">provider:
    # ...
    architecture: arm64
functions:
    # ...</pre><p>The <code>architecture: arm64</code> field can also be set <a href="https://www.serverless.com/framework/docs/providers/aws/guide/functions#instruction-set-architecture">in each function individually</a>.</p><p><strong>Warning:</strong> the example above uses the new <code>runtime: php-xx</code> syntax introduced above. If you set <code>layers</code> instead, you will need to set <code>architecture: arm64</code> <strong>and</strong> update layers to reference ARM layers:</p><pre class="language-yaml">provider:
    # ...
    architecture: arm64
functions:
    api:
        # ...
        layers:
            # Add the `-arm` prefix in layers ?
            - ${bref:layer.arm-php-81-fpm}</pre><h2 id="faster-deployments">Faster deployments</h2><p>There is a <code>serverless.yml</code> option <a href="https://www.serverless.com/framework/docs/providers/aws/guide/deploying#deployment-method">to enable faster deployments</a>:</p><pre class="language-yaml">provider:
    # ...
    deploymentMethod: direct</pre><p>In Bref v2, this option is enabled by default (<a href="https://github.com/brefphp/bref/pull/1395">#1395</a>). If the option was already set in your <code>serverless.yml</code>, you can remove it (or leave it). If it wasn't, your deployments should be about twice faster.</p><h2 id="simpler-cli-commands">Simpler CLI commands</h2><p>Using Bref means using 2 different CLIs:</p><ul><li><code>vendor/bin/bref</code></li>
<li><code>serverless</code></li>
</ul><p>With Bref v2, all commands (except <code>vendor/bin/bref init</code>) have been moved to the <code>serverless</code> CLI (<a href="https://github.com/brefphp/bref/pull/1303">#1303</a>). Besides reducing confusion, integrating in the <code>serverless</code> CLI lets us re-use the same AWS credentials, region, stack names, function names, etc. It makes the commands simpler.</p><p>Here are the commands that have changed:</p><ul><li>
<p><code>vendor/bin/bref cli</code> is replaced by the simpler <code>serverless bref:cli</code>.</p>
<p>For example:</p>
<pre class="language-bash">vendor/bin/bref cli mystack-dev-artisan --region=eu-west-1 -- migrate --force
# becomes:
serverless bref:cli --args="migrate --force"</pre>
<p>No need to provide the function name or the region anymore. Read <a href="https://bref.sh/docs/runtimes/console.html#usage">the Console documentation</a> to learn more. You will also find alternatives if you don't use the <code>serverless</code> CLI.</p>
</li>
<li>
<p><code>vendor/bin/bref local</code> is replaced by the simpler <code>serverless bref:local</code>.</p>
<p>For example:</p>
<pre class="language-bash">vendor/bin/bref local --handler=my-handler.php
# becomes:
serverless bref:local -f hello</pre>
<p>No need to provide the handler file name anymore, we directly use the function name. The new <code>serverless bref:local</code> command has similar arguments as <code>serverless invoke</code>.</p>
<p>Read <a href="https://bref.sh/docs/function/local-development.html">the Local Development documentation</a> to learn more. You will also find alternatives if you don't use the <code>serverless</code> CLI.</p>
</li>
<li>
<p><code>vendor/bin/bref layers</code> is replaced by the simpler <code>serverless layers</code>.</p>
<p>Layer versions are also available at <a href="https://runtimes.bref.sh/">runtimes.bref.sh</a> if you don't use the <code>serverless</code> CLI.</p>
</li>
</ul><p>These changes allowed us to simplify the commands (automatically use the AWS region, credentials and stage from the <code>serverless</code> CLI). It also allowed us to remove the biggest <code>bref/bref</code> Composer dependencies and make the package much lighter.</p><p>Bref v1 lets you inject secrets (API keys, DB passwords, etc.) stored in SSM into environment variables <strong>at deployment time</strong>:</p><pre class="language-yaml">provider:
    # ...
    environment:
        GITHUB_TOKEN: ${ssm:/my-app/github-token}</pre><p>This relies on <code>serverless.yml</code> variables (<a href="https://www.serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-using-the-ssm-parameter-store"><code>${ssm:xxx}</code></a>) and works well, however the drawbacks are:</p><ul><li>The secret value is retrieved on <code>serverless deploy</code> and set in plain text in the environment variable.</li>
<li>The user that runs <code>serverless deploy</code> must have permissions to retrieve the secret value.</li>
</ul><p>In Bref v2, you can have these secrets injected <strong>at runtime</strong> (when your code boots in Lambda) via a new syntax (<a href="https://github.com/brefphp/bref/pull/1376">#1376</a>):</p><pre class="language-yaml">provider:
    # ...
    environment:
        # Different syntax that does NOT start with `$`
        GITHUB_TOKEN: bref-ssm:/my-app/github-token</pre><p>In the example above, <code>GITHUB_TOKEN</code> will be deployed with the string <code>bref-ssm:/my-app/github-token</code> (i.e. it doesn't contain the secret). When Lambda starts, Bref will automatically retrieve the secret and <strong>replace</strong> the environment variable value. No changes needed in your code.</p><p>This offers a more secure solution for teams that prefer to keep secrets as tight as possible.</p><p>Read more about this new feature and secrets in general in the <a href="https://bref.sh/docs/environment/variables.html#at-runtime">Secrets documentation</a>.</p><h2 id="simpler">Simpler <code>docker-compose.yml</code> for local development</h2><p>Running HTTP applications locally with Bref Docker images got simpler (<a href="https://github.com/brefphp/aws-lambda-layers/pull/38">#38</a>). If you used them in <code>docker-compose.yml</code>, you will need to update it.</p><p>Before (Bref v1):</p><pre class="language-yaml">services:
    web:
        image: bref/fpm-dev-gateway
        ports:
            - '8000:80'
        volumes:
            - .:/var/task
        depends_on:
            - php
        environment:
            HANDLER: public/index.php
            DOCUMENT_ROOT: public
    app:
        image: bref/php-80-fpm-dev
        volumes:
            - .:/var/task
    console:
        image: bref/php-80
        volumes:
            - .:/var/task
        entrypoint: php</pre><p>After (Bref v2):</p><pre class="language-yaml">services:
    app:
        image: bref/php-80-fpm-dev
        ports: [ '8000:8000' ]
        volumes:
            - .:/var/task
        environment:
            HANDLER: public/index.php
            DOCUMENT_ROOT: public</pre><p>The <code>bref/php-XX-fpm-dev</code> images can now run HTTP applications, console commands as well as event-driven functions too. Read more in <a href="https://bref.sh/docs/web-apps/local-development.html">web app local development</a>.</p><p>The <code>bref/fpm-dev-gateway</code> image is no longer needed, and code running in <code>bref/php-XX-fpm-dev</code> now runs in an environment even closer to production.</p><h2 id="php-constructs-for-aws-cdk-support">PHP constructs for AWS CDK support</h2><p><a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html">AWS CDK</a> is a deployment tool that can be used as an alternative to <code>serverless.yml</code>.</p><p>Bref 2.0 introduces basic support for the AWS CDK (NodeJS) via <a href="https://github.com/brefphp/constructs">PHP constructs</a>.</p><p>In case you are not familiar with it, AWS CDK is bit more complex than <code>serverless.yml</code> to deploy serverless apps. These constructs will be useful to those actively looking to use the CDK with Bref.</p><h2 id="rewritten-internals">Rewritten internals</h2><p>The scripts that build the AWS Lambda runtimes and Docker images have been completely rewritten at least 4 times (<a href="https://github.com/brefphp/aws-lambda-layers">just look at the number of commits on the v2 runtimes…</a>). These scripts have also been moved to a separate repository: <a href="https://github.com/brefphp/aws-lambda-layers">brefphp/aws-lambda-layers</a>.</p><p>The internals are in a much better place now:</p><ul><li>We have way more test coverage.</li>
<li>We optimized the runtime sizes.</li>
<li>We optimized the build speed.</li>
<li>We documented as much as possible.</li>
<li>We now understand 99% of the build scripts (compared to ~50% before, yes I'm not joking).</li>
</ul><p>Besides making maintenance and contributions much simpler, these changes allowed us to support ARM Lambda functions.</p><p>I want to extend a huge thanks to <a href="https://depot.dev/">Depot</a> for sponsoring the project and <a href="https://twitter.com/matthieunapoli/status/1620090744408244224">making our Docker builds 10 to 20 times faster</a>!</p><p>I know this isn't <em>that</em> exciting, but I had to mention it given the incredible effort put into this over the last 1.5 years.</p><h2 id="thanks">Thanks</h2><p>A huge thanks to the <a href="https://github.com/brefphp/bref/graphs/contributors">136 Bref contributors</a>, to the community for supporting the project, and to the open-source sponsors:</p><p>and <a href="https://github.com/sponsors/mnapoli#sponsors">many others</a> (shout out to <a href="https://twitter.com/GensDeConfiance">@GensDeConfiance</a> for the one-time sponsorship today).</p><p>A huge thank you to all contributors that have helped to maintain v1 and/or with this huge upcoming release, including <a href="https://github.com/deleugpn">@deleugpn</a> who rewrote most of the Lambda runtimes, <a href="https://github.com/GrahamCampbell">@GrahamCampbell</a> who is helping to keep the runtimes up to date, <a href="https://github.com/t-richard">@t-richard</a> and <a href="https://github.com/afu-dev">@afu-dev</a> who have been extremely helpful on the Symfony integration and in the community, <a href="https://github.com/Nyholm">@Nyholm</a> who has been maintaining flawlessly the Bref Extra extensions, <a href="https://github.com/mykiwi">@mykiwi</a> for keeping PHP up to date, <a href="https://github.com/shouze">@shouze</a> who is helping on layers and extra extensions, <a href="https://github.com/georgeboot">@georgeboot</a> who is contributing amazing ideas on the Laravel integrations, <a href="https://github.com/tillkruss">@tillkruss</a> who is pushing what Bref could be for Laravel users, and so many more (<a href="https://github.com/shadowhand">@shadowhand</a>, <a href="https://github.com/kevincerro">@kevincerro</a>, <a href="https://github.com/Guillaume-Rossignol">@Guillaume-Rossignol</a>, <a href="https://github.com/nealio82">@nealio82</a>…).</p><p>Thank you all!</p><h2 id="thats-it">That's it!</h2><p>Hope you enjoy Bref v2!</p><p>There is a complete <a href="https://bref.sh/docs/upgrading/v2.html"><strong>v2 Upgrade Guide</strong></a> that you can follow.</p><p>Head to the docs to <a href="https://bref.sh/docs/"><strong>get started with Bref</strong></a>, or check out the documentation for <a href="https://bref.sh/docs/frameworks/laravel.html">Laravel</a> or <a href="https://bref.sh/docs/frameworks/symfony.html">Symfony</a>.</p><p>You can also join the community <a href="https://bref.sh/docs/community.html">in Slack</a>, post details about your project in <a href="https://github.com/brefphp/bref/issues/267">Built with Bref</a>, or share your experience online and mention <a href="https://twitter.com/brefphp">@brefphp</a> on Twitter.</p><p>If you enjoy teasers, here is a preview of a redesign coming soon to Bref:</p><div class="flex justify-center"><img src="https://bref.sh/docs/news/02/logo.png" class="h-48" alt="image" /></div><h2 id="one-more-thing">One more thing</h2><p>I launched the <a href="https://dashboard.bref.sh/"><strong>Bref Dashboard</strong></a> ✨ in January. It helps you monitor and debug Bref applications:</p><p><a href="https://dashboard.bref.sh/?ref=bref"><img src="https://bref.sh/docs/monitoring/bref-dashboard.png" alt="Bref Dashboard" /></a></p><p>And if you need support or help going serverless, check out the <a href="https://bref.sh/#ecosystem">Support Plans</a>.</p><p><a href="https://bref.sh/docs/" class="rounded-md shadow px-8 py-8 border text-center font-bold hover:bg-gray-100">What is Bref and serverless?</a> <a href="https://bref.sh/docs/first-steps.html" class="rounded-md shadow px-8 py-8 border text-center font-bold hover:bg-gray-100">Get started with Bref</a></p><div id="in-page-menu" class="hidden fixed top-0 max-h-screen text-xs text-gray-500 max-w-48 mr-2 c1"><p class="mb-2 uppercase font-semibold tracking-wider">Summary</p></div>]]></summary>
    <link href="https://bref.sh/docs/news/02-bref-2.0.html"/>
    <updated>2023-03-03T01:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/single-file-cdk/</id>
    <title><![CDATA[Simpler single-file AWS CDK deployments]]></title>
    <summary><![CDATA[<div><header class="clearfix">
<p class="article-info text-muted">2 January 2023 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>I am checking out the <a href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html">AWS CDK</a> to deploy serverless applications.</p><p>But having used <a href="https://www.serverless.com/">Serverless Framework</a> a lot, I find the AWS CDK very noisy and invasive in some of my projects. It doesn't have to be though.</p><p>Here are my notes on how to use the AWS CDK in a single file, without polluting your existing projects.</p><p><em>Disclaimer: what I am describing is only interesting is some projects, where the CDK is just "a deployment tool". I understand that some other projects can use the CDK as a framework for the whole app and the "invasiveness" is a feature.</em></p><h2>The standard AWS CDK setup</h2><p>If you have an existing project that you want to deploy using the AWS CDK, tough luck. You can't easily "introduce" the CDK in an existing project: <code>cdk init</code> only works on an empty directory.</p><p>But let's move past that. Let's create a new TypeScript CDK project:</p><pre class="language-bash">cdk init app --language=typescript myapp</pre><p>Here is what we have:</p><pre>bin/
    cdk-myapp.ts
lib/
    cdk-myapp-stack.ts
test/
    cdk-myapp.test.ts
cdk.json
README.md
package.json
package-lock.json
tsconfig.json
jest.config.js</pre><p>With that setup, we can deploy with <code>npx cdk deploy</code>.</p><h2>Trimming the fat</h2><p>If we look only at what makes the CDK actually work, we have:</p><pre>bin/
    cdk-myapp.ts
lib/
    cdk-myapp-stack.ts
cdk.json
tsconfig.json</pre><p>When <code>cdk deploy</code> runs, it executes the command listed in <code>cdk.json</code>:</p><pre class="language-json">{
    "app": "npx ts-node --prefer-ts-exts bin/cdk-myapp.ts"
}</pre><p>Cool, it uses <code>ts-node</code> to compile TypeScript on the fly. At least we don't have to run a build step and deal with compiled files. It also means we can get rid of <code>tsconfig.json</code> if we don't need it for the rest of our app.</p><p>It also means we can change the <code>bin/cdk-myapp.ts</code> file. Let's move it to the root and rename it to <code>cdk.ts</code>:</p><pre>lib/
    cdk-myapp-stack.ts
cdk.ts
cdk.json</pre><p>Now let's look at <code>cdk.ts</code>:</p><pre class="language-ts">#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkMyappStack } from '../lib/cdk-myapp-stack';
const app = new cdk.App();
new CdkMyappStack(app, 'CdkMyappStack', {
  /* loads of comments here... */
});</pre><p>And <code>lib/cdk-myapp-stack.ts</code>:</p><pre class="language-ts">import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkMyappStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // The code that defines your stack goes here
  }
}</pre><p>Let's inline all the CDK code in <code>cdk.ts</code>:</p><pre class="language-ts">#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
class CdkMyappStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        // The code that defines your stack goes here
    }
}
const app = new cdk.App();
new CdkMyappStack(app, 'CdkMyappStack', {
  /* loads of comments here... */
});</pre><p>Finally, let's inline the stack class:</p><pre class="language-ts">#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
const app = new cdk.App();
new class extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        // The code that defines your stack goes here
    }
}(app, 'my-app', {
  /* stack options */
});</pre><h2>Final result</h2><p>In the end, the AWS CDK only requires 2 files in our project:</p><pre>cdk.ts
cdk.json</pre><p>Those are easy to add to an existing project! We also still deploy with <code>npx cdk deploy</code> because <code>cdk.json</code> points to <code>cdk.ts</code>:</p><pre class="language-json">{
    "app": "npx ts-node --prefer-ts-exts cdk.ts"
}</pre><p>And finally, all our infrastructure code is defined in <code>cdk.ts</code>:</p><pre class="language-ts">#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
const app = new cdk.App();
new class extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        // The code that defines your stack goes here
    }
}(app, 'my-app', {
  /* stack options */
});</pre><h2>Going further</h2><p>We could imagine a helper function for simple apps:</p><pre class="language-ts">#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
cdkStack('my-app', (scope: Construct, id: string, props?: cdk.StackProps) =&gt; {
    // The code that defines your stack goes here
});</pre><p>That <code>cdkStack</code> helper would create the <code>App</code> and the <code>Stack</code> for us.</p><p>I'm tempted to it as an open-source package… Let me know what you think of the idea!</p></div><h2>Comments</h2>]]></summary>
    <link href="https://mnapoli.fr/single-file-cdk/"/>
    <updated>2023-01-02T17:32:00+01:00</updated>
  </entry>
  <entry>
    <id>https://www.serverless.com/blog/serverless-framework-compose-multi-service-deployments</id>
    <title><![CDATA[Introducing multi-service deployments via Serverless Framework Compose]]></title>
    <summary><![CDATA[<figure class="w-richtext-align-fullwidth w-richtext-figure-type-video c1"><iframe allowfullscreen="allowfullscreen" frameborder="0" scrolling="no" src="https://www.youtube.com/embed/hbFSfI_Y4rU" title="Introducing multi-service deployments via Serverless Framework Compose"> </iframe>
</figure><p>We are excited to announce Serverless Framework Compose: a new feature enabling you <strong>to deploy multiple services in one command, in parallel, or ordered by dependencies!</strong></p><p>To use this new feature, <a href="https://www.serverless.com/framework/docs/getting-started#upgrade">upgrade the serverless CLI</a> to v3.15 or greater and follow the guide below.</p><h2><strong>Composing multiple Serverless Framework services</strong></h2><p>Deploying multiple services in a monorepository is a very common pattern across larger teams. Orchestrating these deployments can become painful.</p><p>Let's take the following (simple) example project:</p><div class="w-embed"><pre>my-app/
  products/
    src/
    serverless.yml
  orders/
    src/
    serverless.yml</pre></div><p>Our application contains 2 services. Each service can be deployed separately by running serverless deploy in each folder.</p><div class="w-embed"><p>With Serverless Framework Compose, it is possible <strong>to deploy all those Serverless Framework services via a single <code>serverless deploy</code> command.</strong> To do so, create a <code>serverless-compose.yml</code> file at the root:</p></div><div class="w-embed"><pre>my-app/
  serverless-compose.yml
  products/
    src/
    serverless.yml
  orders/
    src/
    serverless.yml</pre></div><p>The new "serverless-compose.yml" configuration file references the existing Serverless Framework projects:</p><div class="w-embed"><pre class="language-yaml hljs"># serverless-compose.yml
services:
  products:
    path: products
  orders:
    path: orders</pre></div><p>As you can see above, each existing Serverless Framework service is referenced via a relative path. When running "serverless deploy" in the root directory, Compose will deploy both services ("products" and "orders") in parallel.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/62600c2f9e1d7a45bfc5285b_625d776f2e76efce744b071c_blog%2520inline.png" alt="" /></div>
</figure><p>While Compose triggers the deployment, each deployment runs as usual, in the exact same conditions as a traditional Serverless Framework deployment. This was designed so that introducing Compose in an existing project would have no side-effect.</p><div class="w-embed"><p>Note that it is also possible to deploy all services to a specific stage with the <code>--stage</code> option.</p></div><div class="w-embed"><h2>Solving service dependencies</h2><p>Sometimes, services have dependencies between them. The output of one service (for example its API URL, a table name, a queue URL, etc.) might be used by another.</p><p>Passing outputs between dependencies no longer requires complex solutions with CloudFormation imports. In the <code>serverless-compose.yml</code> file, you can now pass outputs from one service to another:</p><pre class="language-yaml" lang="yaml"># serverless-compose.yml
services:
  products:
    path: products
  orders:
    path: orders
    params:
      productsTableName: ${products.tableName}
</pre><p>Let's break it down into 3 steps:</p><ol><li>
<p><code>${products.tableName}</code> will resolve to the "tableName" output of the "products" service.</p>
<p>The outputs of a Serverless Framework service are taken from its CloudFormation outputs. Here is how we can expose the "queueUrl" output in the <code>products/serverless.yml</code> config:</p>
<pre class="language-yaml" lang="yaml"># products/serverless.yml
...
resources:
  Resources:
    Table:
      Type: AWS::DynamoDB::Table
      ...
  Outputs:
    tableName:
      Value: !Ref Table
</pre></li>
<li>
<p>Because of the dependency introduced by the variable, <code>serverless deploy</code> will automatically <strong>order deployments</strong> and deploy "products" first, and then "orders".</p>
</li>
<li>
<p>The value will then be passed to the "orders" service as a parameter named "productsTableName". <a href="https://www.serverless.com/framework/docs/guides/parameters">Parameters can be referenced</a> in Serverless Framework configuration:</p>
<pre class="language-yaml" lang="yaml"># orders/serverless.yml
provider:
  ...
  environment:
    # Inject the value in a function environment variable
    PRODUCTS_TABLE: '${param:productsTableName}'
</pre></li>
</ol><p>Compose is designed to integrate with existing <code>serverless.yml</code> features (CloudFormation outputs for outputs, <a href="https://www.serverless.com/framework/docs/guides/parameters">Serverless Framework parameters</a> for inputs). That way, using Compose does not require a huge rearchitecture of existing monorepositories.</p></div><h3><strong>Setting dependencies without variables</strong></h3><p>Using variables allows us to exchange values <em>and</em> order deployments. It is also possible to order deployments without variables via the "dependsOn" feature:</p><div class="w-embed"><pre class="language-yaml hljs"># serverless-compose.yml
services:
  products:
    path: products
  orders:
    path: orders
    dependsOn:
      - products</pre></div><p>In the example above, the "products" service will be deployed before "orders". This can be useful in scenarios where a service interacts with another and we want to make sure API or schema changes are deployed in a specific order.</p><h2><strong>Global commands and service commands</strong></h2><p>An interesting benefit of grouping multiple services behind a single configuration is that <strong>we can run one command in all services</strong> at once. For example:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/62600c2f33e4cc64f2a08f4a_625d7b122607b3303ebe0ea4_blog%2520inline%2520commands.png" alt="" /></div>
</figure><p>We can also run <strong>service-specific commands</strong> without having to change directories:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/62600c2f44de8c7a56070e9e_625d7cc6f7d029aaf877857c_blog%2520service-specific%2520commands.png" alt="" /></div>
</figure><h2>What's next</h2><p>Serverless Framework now lets you compose services to orchestrate their deployments.</p><p>Services can integrate together via variables and are deployed in order based on their dependencies.</p><p>There is a lot more to Serverless Framework Compose, <a href="https://www.serverless.com/framework/docs/guides/compose">head over to the documentation</a> to learn more and get started.</p><p>We are also exploring exciting ideas to improve Compose in the near future, like:</p><ul role="list"><li>accelerating deployments for services that haven't changed</li>
<li>deploying only specific services via a filter flag</li>
<li>multi-region deployments</li>
</ul><p>Get involved in the <a href="https://github.com/serverless/serverless">Serverless Framework repository</a> to discuss these ideas!</p>]]></summary>
    <link href="https://www.serverless.com/blog/serverless-framework-compose-multi-service-deployments"/>
    <updated>2022-04-20T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://www.serverless.com/blog/aws-lambda-function-urls-with-serverless-framework</id>
    <title><![CDATA[AWS Lambda Function URLs with Serverless Framework]]></title>
    <summary><![CDATA[<p>AWS just released a brand new feature called "<a href="http://aws.amazon.com/blogs/aws/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices">Lambda Function URLs</a>", and we are happy to announce that <strong>Serverless Framework supports Lambda Function URLs immediately</strong>.</p><p>A Lambda Function URL is a <strong>simple solution to create HTTP endpoints with AWS Lambda</strong>. Function URLs are ideal for getting started with AWS Lambda, or for single-function applications like webhooks or APIs built with web frameworks.</p><p>While not a complete replacement for API Gateway, which provides more features, a Function URL is a simpler alternative with no extra cost and a much larger timeout limit. We will explore the pros and cons in detail in this article.</p><p>Using Lambda Function URLs with Serverless Framework can be done via a single statement:</p><div class="w-embed"><pre class="hljs language-yaml">functions:
  hello:
    handler: handler.hello
    url: true
</pre></div><p>To use that new feature, <a href="https://www.serverless.com/framework/docs/getting-started#upgrade">upgrade Serverless Framework</a> (v3.12.0 or greater).</p><h2>Lambda URLs in details</h2><p>Function URL is a new feature of AWS Lambda <a href="http://aws.amazon.com/blogs/aws/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices">that exposes a function as an HTTPS endpoint</a>.</p><div class="w-embed"><p>To deploy a function with a URL, use the <code>url</code> property in <code>serverless.yml</code>:</p></div><div class="w-embed"><pre class="hljs language-yaml">functions:
  hello:
    handler: handler.hello
    url: true
</pre></div><div class="w-embed"><p>When running <code>serverless deploy</code>, the URL will be created and displayed in the terminal output. The URL will look like this:</p></div><div class="w-embed"><p><code>https://yti4le2qudflx43hh.lambda-url.eu-south-1.on.aws/</code></p></div><p>That domain is unique to the Lambda function, and any URI path will invoke the function handler.</p><p>The request and response event payloads follow the same format as <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html">API Gateway's HTTP API "version 2" payload format</a>. That means it is possible to switch from a Lambda Function URL to API Gateway's HTTP API without any code change.</p><p>Here is a simple NodeJS Lambda handler that responds to a Function URL:</p><div class="w-embed"><pre class="hljs language-js">exports.handler = async (event) =&gt; {
  return {
    statusCode: 200,
    body: 'Hello world!',
  };
};
</pre></div><h3>Endpoint configuration</h3><p>The endpoint is public by default. It can also be protected by IAM authentication:</p><div class="w-embed"><pre class="hljs language-yaml">functions:
  hello:
    handler: handler.hello
    # Public by default
    url: true
  world:
    handler: handler.hello
    # This URL is protected by IAM authentication
    url:
      authorizer: aws_iam
</pre></div><p>CORS can also be configured on the Function URL:</p><div class="w-embed"><pre class="hljs language-yaml">functions:
  hello:
    handler: handler.hello
    url:
      # Allow CORS for all requests from any origin
      cors: true
  world:
    handler: handler.hello
    url:
      # Configure CORS in details:
      cors:
        allowCredentials: …
        allowedHeaders: …
        allowedMethods: …
        allowedOrigins: …
        exposedResponseHeaders: …
        maxAge: ….
</pre></div><p>View the complete list of options for configuring CORS in the <a href="https://www.serverless.com/framework/docs/providers/aws/events/http-api/#cors-setup">HTTP API event documentation</a>.</p><p>Unlike API Gateway, Function URLs do not have a maximum execution time: the execution time is limited by the Lambda function's timeout (up to 15 minutes).</p><p>Note that Function URLs do not support custom domains. The solution would be to use CloudFront with the Function URL as the origin.</p><h2>Function URLs vs. API Gateway</h2><p>Lambda Function URLs let us create an HTTP endpoint for a Lambda function. Let's address the obvious question: how is that different from API Gateway?</p><p><strong>Function URLs send all HTTP requests to one Lambda function.</strong> This pairs well with backend frameworks like Express or Apollo (Node), Flask (Python), etc. Indeed, these frameworks can handle HTTP routing, authentication, middlewares, and more. Function URLs also work great for single-purpose endpoints like webhooks.</p><p>While simple, the downside of this approach is that the whole API is contained in a single Lambda function: this is the "<em>Mono-Lambda</em>" pattern. The deployed package size could be big, debugging could be complex, and permissions are less granular than they could be. You can read more about that topic in <a href="https://dev.to/aws-builders/the-what-why-and-when-of-mono-lambda-vs-single-function-apis-5cig">The What, Why, and When of Mono-Lambda vs Single Function APIs</a>.</p><p><strong>API Gateway filters, processes, and routes requests to different Lambda functions.</strong> This removes the need for using backend frameworks. API Gateway can take care of transforming requests and responses, authenticating clients with different mechanisms, supporting custom domains, managing API access via API keys, and much more.</p><p>Note that the exact list of API Gateway features varies between v1 (REST API) and v2 (HTTP API): read <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html">Choosing between HTTP APIs and REST APIs</a> to learn more.</p><p>While API Gateway provides more features, it also comes with added complexity and costs. Beyond the free tier, API Gateway v1 starts at $3.5/million requests and v2 at $1/million requests. Unlike API Gateway, Lambda Function URLs do not add any cost.</p><p>Regarding performance, one could expect Function URLs to add less latency than API Gateway. In practice, the difference is minimal. Here is the latency I measured with a quick test:</p><ul role="list"><li>Function URL adds 9 to 10ms to the HTTP response time</li>
<li>API Gateway v2 adds 12 to 15ms to the HTTP response time</li>
</ul><p>The latency mentioned above is added to the Lambda execution duration.</p><p>Let's compare all this in detail:</p><table class="c2"><thead class="c1"><tr>
<th>API Gateway REST</th>
<th>API Gateway HTTP</th>
<th>Function URL</th>
</tr></thead><tbody><tr><td>Pricing</td>
<td>Lambda + API Gateway$3.5/million requests</td>
<td>Lambda + API Gateway$1/million requests</td>
<td>Lambda only (no extra costs)</td>
</tr><tr><td>Authorizers</td>
<td>IAM, Lambda, Cognito</td>
<td>IAM, Lambda, Cognito, OAuth 2</td>
<td>IAM</td>
</tr><tr><td>Custom domain</td>
<td>Yes</td>
<td>Yes</td>
<td>No (but possible with CloudFront)</td>
</tr><tr><td>API key management</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr><tr><td>Caching</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr><tr><td>Request transformation</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr><tr><td>Request/response validation</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr><tr><td>CORS</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr><tr><td>CloudWatch access logs</td>
<td>Yes</td>
<td>Yes</td>
<td>No</td>
</tr><tr><td>CloudWatch metrics</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr><tr><td>Maximum HTTP response timeout</td>
<td>29s</td>
<td>29s</td>
<td>15 minutes</td>
</tr></tbody></table><h2>Use cases</h2><p>Based on the comparison with API Gateway, we can conclude that Lambda Function URLs could be great for scenarios like:</p><ul role="list"><li>APIs built with backend frameworks, like ExpressJS/Apollo/Flask…</li>
<li>Simple webhooks, where API Gateway could be overkill.</li>
<li>Server-side rendered websites, built with CloudFront + Lambda (skipping API Gateway).</li>
<li>APIs with a response time longer than 29 seconds (API Gateway's maximum timeout), for example to import or process data, or long-polling scenarios.</li>
</ul><p>It's also worth keeping in mind that Lambda Function events are compatible between Function URLs and API Gateway's HTTP APIs. That means that Function URLs could be a great way to get started with HTTP on Lambda, and later upgrade to API Gateway to use its features.</p><p>Those are just ideas, we can't wait to see what you build with it! Check out <a href="https://www.serverless.com/framework/docs/providers/aws/guide/functions#lambda-function-urls">the Serverless Framework documentation for Lambda Function URLs</a> to get started.</p><p>‍</p>]]></summary>
    <link href="https://www.serverless.com/blog/aws-lambda-function-urls-with-serverless-framework"/>
    <updated>2022-04-06T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://www.serverless.com/blog/serverless-framework-v3-is-live</id>
    <title><![CDATA[Serverless Framework v3 is live!]]></title>
    <summary><![CDATA[<p>We are excited to announce the release of Serverless Framework v3.</p><p>This new major version brings <strong>a cleaner and redesigned CLI experience</strong> as well as a brand new feature: <strong>stage parameters</strong>.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c1"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f3300e602de28a56e8_6193d008f4d92d593c0e6fb1_6193c1d7c81caf0fa82bbe65_terminal-light.gif" alt="" /></div>
</figure><p>On top of that, we've worked on cleaning up the dependencies to make the serverless package 40% lighter and get rid of NPM security warnings.</p><h2><strong>Upgrading</strong></h2><p>Before we dive into the new features, let's talk about upgrading from v2 to v3.</p><p>As mentioned <a href="https://www.serverless.com/blog/serverless-framework-v3-beta">in the v3 beta announcement</a>, we have revisited many deprecations and breaking changes <strong>to make the upgrade to v3 easier</strong>.</p><p>To upgrade safely, follow these 3 steps:</p><ol role="list"><li><a href="https://www.serverless.com/framework/docs/getting-started#upgrade">Upgrade to the latest v2 version</a> (e.g. via "npm -g install serverless@2")</li>
<li>Fix any deprecation you encounter when deploying with v2</li>
<li>When there are no deprecations left, you are safe to upgrade to v3:</li>
</ol><div class="w-embed"><pre class="hljs language-bash">$ npm -g install serverless
...
# Check the version
$ serverless --version
Framework Core: 3.0.0
</pre></div><p>If you installed serverless as a standalone binary, <a href="https://www.serverless.com/framework/docs/getting-started#standalone-binary">read these instructions instead</a>.</p><p>You can read the <a href="https://www.serverless.com/framework/docs/guides/upgrading-v3"><strong>complete "Upgrading to v3" guide</strong></a> to read about all breaking changes and instructions for specific cases.</p><p>It is also possible to use both v2 and v3 in different projects. Read more about this in <a href="https://www.serverless.com/framework/docs/guides/upgrading-v3#using-v2-and-v3-in-different-projects">the v3 upgrade guide</a>.</p><p>Finally, if you are looking to get started with Serverless Framework v3, check out our new <a href="https://www.serverless.com/blog/getting-started-with-serverless-framework">Getting Started guide</a>.</p><h2><strong>Stage parameters</strong></h2><p>Serverless Framework v3 introduces "stage parameters". It allows changing the service configuration based on the current stage.</p><p>This is particularly useful when deploying services to multiple environments, like a development/staging environment and a production environment.</p><p>Parameters can be defined under the new params key, and can be used via "${param:xxx}" variables:</p><div class="w-embed"><pre class="language-yaml hljs"># serverless.yml
service: myapp
provider:
  name: aws
  environment:
    APP_DOMAIN: ${param:domain}
params:
  prod:
    domain: myapp.com
  dev:
    domain: preview.myapp.com
</pre></div><p>In the example above, the "${param:domain}" variable will resolve to:</p><ul role="list"><li>myapp.com for the "prod" stage</li>
<li>preview.myapp.com for the "dev" stage</li>
</ul><p>It is also possible to define default parameter values via the default key. These values will apply to all the other stages:</p><div class="w-embed"><pre class="language-yaml hljs">params:
  default:
    domain: ${sls:stage}.preview.myapp.com
  prod:
    domain: myapp.com
  dev:
    domain: preview.myapp.com
</pre></div><p>Note that this new feature is born out of a common pattern: using the "custom" section with nested variables. For example:</p><div class="w-embed"><pre class="language-yaml hljs">provider:
  environment:
        APP_DOMAIN: ${self:custom.stages.${sls:stage}.domain}
</pre></div><p>If you are already using this pattern, we hope the new stage parameters can help simplify your configuration and make it more maintainable!</p><p>Finally, thanks to the optional integration with Serverless Dashboard, you can also store secret values securely and retrieve them via the "${param:my-secret}" variable syntax.</p><p>Learn everything about stage parameters in the <a href="https://www.serverless.com/framework/docs/guides/parameters"><strong>Parameters documentation</strong></a>.</p><h2><strong>Redesigned CLI experience</strong></h2><p>Over the years, Serverless Framework has become the most advanced tool to create and deploy serverless applications.</p><p>This comes with a challenge: maintaining a clean and simple experience for users. As deprecations, plugins, and cloud resources multiply, so does the noisiness of the CLI.</p><p>Serverless Framework v3 is the framework you know and love, <strong>with a reimagined interface</strong>. We started from scratch and asked ourselves: "as a user, what do I <em>need</em> to know?" at each step of each command. The new design:</p><ul role="list"><li>focuses on actionable information</li>
<li>removes all extra noise</li>
<li>is easier on the eyes with minimalistic colors and styles</li>
</ul><p>Below is a preview of the new design with the most common commands.</p><h2>Deploy</h2><p>The "serverless deploy" command now features a clean and minimal output. Here is a comparison of v2 (left) and v3 (right):</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/61f2c9c9629f9764259bd93c_61f020a3c8d64d21bbf40e47_CvcZ4oI_ppKsgXaId6Hh0G_mI-CUN6N3hGlbYxn6ZCuGI1RE0-aMYWKBti9m9Lnx3f-_V3GDognqJwIvopIrpuQ9c_wT3Ss6L7jPVjdFvvqqBPIfuSJ9NgBZI6HIV4CP-Q06gn3x.png" alt="" /></div>
</figure><h2>Verbose output</h2><p>Serverless Framework v3 now supports the standard "--verbose" flag to output more details.</p><p>That option can be particularly useful in CI/CD, for example to get a detailed history of the CloudFormation deployment:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/61f2c9c8c4dd09d87f6c9433_61f020a390784a5942d16846_7ycRdZzQaKG4etYxS-Gh1gDDN8JP1D2_YlW2TroFN2-o7k8fKvavXVaQ7IO-kKefCXI_izd22kN4r7WcBr9CIUsltPiktqHfXd57VqDZzVGsFy2nyBd-6Ec5tre-KP7FecDdK2Jg.png" alt="" /></div>
</figure><h2>Clear errors</h2><p>The error screen has been improved: any failure is now clearly signaled, secondary information is toned down and the error message is printed last, to appear right above the command prompt.</p><p>On top of that, CloudFormation errors now contain more details about resources and their statuses:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/61f2c9c8d5800359542362d5_61f020a32eb5173bdc1aa0ca_Dil33giVaPXBtsUpPbxZ_YaxmB-BAf3GtqUJoKlo5vl0PEIikFO2fPmWxexs6Ez06Ig7AyFuw-mdIzpm29bU-gT8qZjrlgRx7lyrwdqAe31R5-efU7JXhQ-Y7UPT6_-PzHo4Letl.png" alt="" /></div>
</figure><h2>Cleaner logs</h2><p>The "serverless logs" command now features a cleaner and lighter output, that brings more focus on the content of the logs.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/61f2c9c9a3c9939ff8c92da2_61f020a42c334339b8a35cde_xwu5pijvJgxZb2NQpvvJxjVgrZKRB6kw1Sr2QaTv7-2_QDHN_sgtkuvm-R1fF0qeVVrhoyOhQIbYgit7fRyOLnU2MQy9B-I68Fx4qSUwe8YUiGDB4fM46DCGW3w7Hh6ybN1SfkCI.png" alt="" /></div>
</figure><h2>New serverless onboarding</h2><p>Serverless Framework can now interactively set up new projects: just run "serverless" in an empty directory and follow the prompt.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/61f2c9c8a9489bc4d0150d69_61f020a3aad16b07ddd12b27_jbEvnmEZSwteoxa6N1OPk6Jbq-jDjvBksisN-8-3iaJtmREjvwPQYhRYOgbcEHMjpElJ-Ku2SAzVhfN3S1YKy3EupNS-fRxjhNyNREpM10eL_fqU6u8NFrUTFd3Fkkk4T1H4Oog9.png" alt="" /></div>
</figure><p>The interactive setup also lets you set up the <a href="https://www.serverless.com/monitoring">Serverless Dashboard</a> in a few steps. Run "serverless" in an existing project and get access to premium monitoring, AWS account management, parameters, and more.</p><h2><strong>Upgrading plugins</strong></h2><p>We have worked hard at helping plugins be ready for Serverless Framework v3.</p><p>That being said, given the size of the ecosystem, we have identified 3 categories of plugins:</p><ol role="list"><li>Plugins that are compatible with v3 and integrate with the new CLI design.</li>
<li>Plugins that are compatible with v3.</li>
<li>Plugins that are not compatible with v3 yet.</li>
</ol><p>Fortunately, most of the plugins are in categories 1 or 2. Some plugins might not integrate fully with the new design yet, but they should work fine.</p><p><strong>We want to help developers take their plugins to the next level!</strong> This is why v3 comes with:</p><p>If you need help updating your plugin, <a href="https://github.com/serverless/serverless/discussions/10241">jump in the GitHub discussion</a> and let us know.</p><h2><strong>Architectural Guidance &amp; Commercial Support</strong></h2><p>While Serverless Framework makes it easy to create radically efficient cloud apps, nothing beats the confidence you’ll gain from working with the team that built the Serverless Framework.</p><p>Serverless Inc's support offering includes architectural reviews to highlight improvements and standards you can leverage to scale projects and teams.  Our support offering also features a private Slack channel where you can interact directly with our team and discuss plugins, the Framework and serverless architectures on AWS.  Consider us your partner in serverless success. </p><p><a href="https://www.serverless.com/support">Learn more about Serverless Premium Support</a>.</p>]]></summary>
    <link href="https://www.serverless.com/blog/serverless-framework-v3-is-live"/>
    <updated>2022-01-27T01:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://www.serverless.com/blog/improved-sqs-batch-error-handling-with-aws-lambda</id>
    <title><![CDATA[Improved SQS batch error handling with AWS Lambda]]></title>
    <summary><![CDATA[<p>AWS announced <a href="https://aws.amazon.com/fr/about-aws/whats-new/2021/11/aws-lambda-partial-batch-response-sqs-event-source/">partial batch response for AWS Lambda and SQS</a>. With this new feature, processing SQS messages in batch is now much more robust, which we'll explore in detail.</p><p>We are happy to say that <strong>Serverless Framework supports that new feature immediately</strong> via the new <strong><em>functionResponseType</em></strong> option:</p><div class="w-embed"><pre class="hljs">functions:  
  worker:
    handler: worker.handler    
    events:      
      - sqs:          
          arn: &lt;ARN of the SQS queue&gt;        
          batchSize: 10
          functionResponseType: ReportBatchItemFailures</pre></div><p><a href="https://www.serverless.com/framework/docs/getting-started#upgrade">Upgrade to v2.67.0 or greater</a> to start using it. In case you are <a href="https://www.serverless.com/blog/serverless-framework-v3-beta">trying out Serverless Framework v3 beta</a>, make sure to update the v3 beta with "npm -g i serverless@pre-3".</p><h3><strong>The challenge with batch processing</strong></h3><p>Up until now, it was already possible to process SQS messages in batch with AWS Lambda, but it had limitations. Here is an example:</p><div class="w-embed"><pre class="hljs"># serverless.yml
service: my-app
provider:  
  name: aws
functions:  
  worker:    
    handler: worker.handler    
    events:      
      - sqs:
          arn: &lt;ARN of the SQS queue&gt;
          batchSize: 10</pre></div><p>Our <strong>worker.js</strong> file would contain a handler like this:</p><div class="w-embed"><pre class="hljs"># worker.js
exports.handler = async function(event) {  
  event.Records.forEach(record =&gt; {    
    const bodyData = JSON.parse(record.body);    
    console.log(`Processing ${record.messageId}`);    
    // ...  
  });
}
</pre></div><p>The challenge is dealing with errors: if any SQS record fails to be processed (i.e. the code handling it throws an exception), then the Lambda function execution fails and the whole batch of messages is considered failed.</p><p>That also means that <strong>if one message fails, the whole batch is retried again</strong>, including messages in the batch that were successfully processed.</p><p>The SQS + Lambda integration provided no practical way to deal with those scenarios. Indeed, catching errors that occurred when processing would mean marking the whole batch of messages as "successfully processed", which was also wrong.</p><h3><strong>AWS Lambda partial batch responses</strong></h3><p>The new "partial batch response" feature lets us signal, from AWS Lambda, which SQS messages have been successfully processed, and which have failed.</p><p>Let's fix the previous example:</p><div class="w-embed"><pre class="hljs">functions:  
  worker:    
    handler: worker.handler    
    events:      
      - sqs:          
          arn: &lt;ARN of the SQS queue&gt;
          batchSize: 10
          functionResponseType: ReportBatchItemFailures</pre></div><p>In <strong>worker.js</strong>, we can now catch errors and report failed messages in the return value of our function:</p><div class="w-embed"><pre class="hljs"># worker.js
exports.handler = async function(event) {  
  const failedMessageIds = [];  
  event.Records.forEach(record =&gt; {    
    try {      
      const bodyData = JSON.parse(record.body);      
      console.log(`Processing ${record.messageId}`);      
      // ...    
    } catch (e) {      
        failedMessageIds.push(record.messageId);    
    }  
  });
  return {    
    batchItemFailures: failedMessageIds.map(id =&gt; {      
      return {        
        itemIdentifier: id      
      }    
    })  
  }
}
</pre></div><p>One downside of this approach is that we can no longer monitor the "error rate" metric of AWS Lambda to monitor errors. Indeed, the Lambda function will execute successfully in all cases (because we catch errors).</p><p>But the upside is that only failed messages will be retried by SQS.</p><p>If you want to learn more, <a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting">check out the official AWS documentation</a>.</p>]]></summary>
    <link href="https://www.serverless.com/blog/improved-sqs-batch-error-handling-with-aws-lambda"/>
    <updated>2021-11-30T01:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://www.serverless.com/blog/serverless-framework-v3-beta</id>
    <title><![CDATA[Announcing Serverless Framework v3 Beta]]></title>
    <summary><![CDATA[<p>Since the release of AWS Lambda, and later Serverless Framework, serverless technologies have radically simplified application development and operations. 6 years later, Serverless Framework <a href="https://www.datadoghq.com/state-of-serverless/#7">is used by 90% of organizations that have gone serverless</a>.</p><p>Today, we are very excited to announce the availability of Serverless Framework v3 beta! The 3.0 release, planned for Q1 2022, includes <strong>a cleaner and redesigned CLI experience that prioritizes actionable information</strong>.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c1"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f3300e602de28a56e8_6193d008f4d92d593c0e6fb1_6193c1d7c81caf0fa82bbe65_terminal-light.gif" alt="" /></div>
</figure><p>The v3 beta lets you experience the new CLI design right now and helps prepare for the upgrade.</p><p>You can install v3 beta globally via NPM:</p><div class="w-embed"><pre class="language-bash hljs">$ npm -g i serverless@pre-3
...
# Check the version
$ serverless --version
Framework Core: 3.0.0-pre.xxxx
</pre></div><p>Or install in a specific project only:<strong>‍</strong></p><div class="w-embed"><pre class="language-bash hljs">$ npm i serverless@pre-3
# Run serverless using npx to avoid conflicts with the global v2 CLI
$ npx serverless --version
Framework Core: 3.0.0-pre.xxxx
</pre></div><h3>Raising the bar on developer experience</h3><p>Over the years, Serverless Framework has become the most advanced tool to create and deploy serverless applications.</p><p>This comes with a challenge: maintaining a clean and simple experience for users. As deprecations, plugins, and cloud resources multiply, so does the noisiness of the CLI.</p><p>Serverless Framework v3 is the framework you know and love, <strong>with a reimagined interface</strong>. We started from scratch and asked ourselves: "as a user, what do I <em>need</em> to know?" at each step of each command. The new design:</p><ul role="list"><li>focuses on actionable information</li>
<li>removes all extra noise</li>
<li>is easier on the eyes with minimalistic colors and styles</li>
</ul><p>Below is a preview of the new design with the most common commands.</p><h4>Deploy</h4><p>The "serverless deploy<em>"</em> command now features a clean and minimal output. Here is a comparison of v2 (left) and v3 (right):</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c2"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f24d3c74056dd516f5_6193d00872d373ed61e59f11_6193c41e73009c8510741ae6_v2-v3.png" alt="" /></div>
</figure><h4>Verbose output</h4><p>Serverless Framework v3 now supports the standard "--verbose" flag to output more details.</p><p>That option can be particularly useful in CI/CD, for example to get a detailed history of the CloudFormation deployment:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c3"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f23796790025c86f49_6193d009c9880aabda38b042_6193c483b299822b91051229_verbose.png" alt="" /></div>
</figure><h4>Clear errors</h4><p>The error screen has been improved: any failure is now clearly signaled, secondary information is toned down and the error message is printed last, to appear right above the command prompt.</p><p>On top of that, CloudFormation errors now contain more details about resources and their statuses:</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c3"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f252858213b94f2a6c_6193d009c34200dd53e469c9_6193c4b1c5511933f6bb598a_error.png" alt="" /></div>
</figure><h4>Cleaner logs</h4><p>The "serverless logs" command now features a cleaner and lighter output that brings more focus on the content of the logs.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c3"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f2c290b41efec9a219_6193d0081ca0963b4ecdb04a_6193c4edb4c59245400cbff6_logs.png" alt="" /></div>
</figure><h4>New Serverless onboarding</h4><p>Serverless Framework can now interactively set up new projects: just run "serverless" in an empty directory and follow the prompt.</p><figure class="w-richtext-align-fullwidth w-richtext-figure-type-image c3"><div><img src="https://assets-global.website-files.com/60acbb950c4d66d0ab3e2007/6193d4f273009cdc92749b30_6193d00864b7ed38900eb26b_6193c5273796790c39c82535_onboarding.png" alt="" /></div>
</figure><p>The interactive setup also lets you set up the <a href="https://www.serverless.com/monitoring">Serverless Dashboard</a> in a few steps. Run serverless in an existing project and get access to premium monitoring, AWS account management, parameters, and more.</p><p>And for those of you that followed <a href="https://www.serverless.com/blog/introducing-serverless-console">the announcement of Serverless Console last week</a>: yes, Serverless Framework will natively integrate with Serverless Console as soon as it is available!</p><h4>Join the discussion</h4><p>If you have any feedback, we would love to hear it. <a href="https://github.com/serverless/serverless/discussions/10242">Join the GitHub discussion</a> and let us know what you like or how we can improve it!</p><h3><strong>Easy to upgrade</strong></h3><p>With several hundred contributors and versions, Serverless Framework v2 has accumulated improvements, but also deprecations, at a great pace.</p><p>While preparing for v3, we have revisited the list of breaking changes <strong>to make the upgrade to v3 easier</strong>.</p><p>We invite all users <a href="https://www.serverless.com/framework/docs/getting-started#upgrade">to upgrade to the latest v2 version</a>: you may notice that some deprecations are gone. Most notably:</p><ul role="list"><li>package.exclude/package.include syntax will continue to work in v3</li>
<li>provider.iamRoleStatements (and related options) will continue to work in v3</li>
<li>the legacy Lambda version hashing mechanisms will continue to be supported in v3</li>
<li>and more (<a href="https://www.serverless.com/framework/docs/guides/upgrading-v3#deprecated-features-that-will-be-kept-in-v3">read the complete list</a>)</li>
</ul><p>You can read more about all these features we've decided to continue supporting <a href="https://www.serverless.com/framework/docs/guides/upgrading-v3#deprecated-features-that-will-be-kept-in-v3">in v3 in the "Upgrading guide"</a>.</p><p>If the list above doesn't mean anything to you, that's OK. Here is all that you need to know:</p><ol role="list"><li>Upgrade to the latest v2 version</li>
<li>If you see deprecations, take the time to fix them</li>
<li>If you don't see deprecations, your project will work with v3  ?</li>
</ol><p>If you are curious, you can also read the complete <a href="https://www.serverless.com/framework/docs/guides/upgrading-v3">"Upgrading to v3" guide</a>.</p><p>We estimate that around 70% of serverless projects should be able to upgrade to v3 without breaking change. If you need help upgrading, feel free to <a href="https://github.com/serverless/serverless/discussions">open a discussion on GitHub</a>.</p><h3><strong>Calling all plugin developers</strong></h3><p>Serverless Framework wouldn't be the same without its incredible plugin ecosystem. As you may expect, the new CLI design can also benefit plugins!</p><p>This is a call to all plugin developers: <strong>we want to help you build amazing plugins</strong>. As such, v3 comes with:</p><ul role="list"><li>New documentation for "<a href="https://www.serverless.com/framework/docs/guides/plugins/cli-output">Creating plugins</a>", which is complete and up-to-date.</li>
<li><strong>Brand new plugin API</strong> for writing awesome CLI output.</li>
</ul><p>The new plugin API lets plugins integrate with the new design via helpers:</p><ul role="list"><li>Log with sensible log levels and formatting (including --verbose and --debug)</li>
<li>Create interactive progress</li>
<li>Add new sections to serverless info output</li>
<li>And more</li>
</ul><p>You can read <a href="https://www.serverless.com/framework/docs/guides/plugins/cli-output">the documentation about these new Plugin APIs</a>.</p><p>We have also started a community discussion with all plugin authors: <a href="https://github.com/serverless/serverless/discussions/10241">join the GitHub discussion</a> and let us know if you have feedback, questions or if you need help.</p><h3><strong>Architectural Guidance &amp; Commercial Support</strong></h3><p>While Serverless Framework makes it easy to create radically efficient cloud apps, nothing beats the confidence you’ll gain from working with the team that built the Serverless Framework.</p><p>Talk to our experts and members of the product team to get architectural guidance as you’re designing and building your app, and get ongoing support as you go into production via email or Slack.  <a href="https://www.serverless.com/support">Learn more about serverless Enterprise Support</a></p>]]></summary>
    <link href="https://www.serverless.com/blog/serverless-framework-v3-beta"/>
    <updated>2021-11-16T01:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://medium.com/serverless-transformation/serverless-queues-and-workers-designing-lift-d870afdba867</id>
    <title><![CDATA[Serverless queues and workers — Designing Lift]]></title>
    <summary><![CDATA[<article><div class="l"><div class="l"><section><div><div class="ho hp hq hr hs"><div class="ab cm"><div class="fg fh fi fj fk fl fm bg"><div class=""><div class="iw ix iy iz ja"><div class="speechify-ignore ab fs"><div class="speechify-ignore bg l"><div class="jb jc jd je jf ab"><div><div class="ab jg"><a rel="noopener follow" href="https://medium.com/@mnapoli?source=post_page-----d870afdba867--------------------------------"><div><div class="bl" aria-hidden="false"><div class="l jh ji bx jj jk"><div class="l dj"><img alt="Matthieu Napoli" class="l df bx gf gg ff" src="https://miro.medium.com/v2/resize:fill:88:88/1*AsvIQHCqPf_B-uaal5exEA.jpeg" width="44" height="44" /></div></div></div></div></a><a href="https://medium.com/serverless-transformation?source=post_page-----d870afdba867--------------------------------" rel="noopener follow"><div class="jo ab dj"><div><div class="bl" aria-hidden="false"><div class="l jp jq bx jj jr"><div class="l dj"><img alt="Serverless Transformation" class="l df bx bq js ff" src="https://miro.medium.com/v2/resize:fill:48:48/1*9dXX1IfF6H1ISqh3lflktQ.png" width="24" height="24" /></div></div></div></div></div></a></div></div><div class="bm bg l"><div class="ab"><div><div class="jt ab q"><div class="ab q ju"><div class="ab q"><div><div class="bl" aria-hidden="false"><p class="be b jv jw bj"><a class="af ag ah ai aj ak al am an ao ap aq ar jx" rel="noopener follow" href="https://medium.com/@mnapoli?source=post_page-----d870afdba867--------------------------------">Matthieu Napoli</a></p></div></div></div>·<p class="be b jv jw dl"><a class="ka kb ah ai aj ak al am an ao ap aq ar ek kc kd" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fsubscribe%2Fuser%2Ff99d87d77f4&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fserverless-queues-and-workers-designing-lift-d870afdba867&amp;user=Matthieu+Napoli&amp;userId=f99d87d77f4&amp;source=post_page-f99d87d77f4----d870afdba867---------------------post_header-----------">Follow</a></p></div></div></div></div><div class="l ke"><div class="ab fq kf kg kh"><div class="ki kj ab"><div class="be b bf z dl ab kk">Published in<div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/serverless-transformation?source=post_page-----d870afdba867--------------------------------" rel="noopener follow"><p class="be b bf z km kn ko kp kq kr ks kt bj">Serverless Transformation</p></a></div></div></div><div class="h k">·</div></div><div class="ab ae">4 min read<div class="ku kv l" aria-hidden="true">·</div>Apr 30, 2021</div></div></div></div></div><div class="ab fs kw kx ky kz la lb lc ld le lf lg lh li lj lk ll"><div class="h k w es et q"><div class="mb l"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fserverless-transformation%2Fd870afdba867&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fserverless-queues-and-workers-designing-lift-d870afdba867&amp;user=Matthieu+Napoli&amp;userId=f99d87d77f4&amp;source=-----d870afdba867---------------------clap_footer-----------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div></div><div><div class="bl" aria-hidden="false"></div></div><div class="ab q lm ln lo lp lq lr ls lt lu lv lw lx ly lz ma"><div class="h k"><div><div class="bl" aria-hidden="false"></div></div><div class="df uz fq"><div class="l ae"><div class="ab cm"><div class="va vb vc vd ve pi fm bg"><div class="ab"><div class="bl bg" aria-hidden="false"><div><div class="bl" aria-hidden="false"></div></div></div></div></div></div></div><div class="bl" aria-hidden="false" aria-describedby="postFooterSocialMenu" aria-labelledby="postFooterSocialMenu"><div><div class="bl" aria-hidden="false"></div></div></div></div></div></div></div></div><p id="690c" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj"><em class="ot">This article is part of a series on </em><a class="af ou" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank"><em class="ot">Lift</em></a><em class="ot">, an open-source project that simplifies deploying serverless applications.</em></p><figure class="oy oz pa pb pc pd ov ow paragraph-image"><div role="button" tabindex="0" class="pe pf dj pg bg ph"><div class="ov ow ox"><picture></picture></div></div></figure><p id="49f1" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">As introduced in previous articles, <a class="af ou" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank"><strong class="nx hw">Lift</strong></a><strong class="nx hw"> is a Serverless Framework plugin</strong> that simplifies deploying serverless applications.</p><p id="86d4" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">As we approach the first beta planned for May, let’s talk about <strong class="nx hw">queues and workers</strong>:</p><ul class=""><li id="19d7" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">How to deploy production-ready queues and workers on AWS today.</li><li id="6967" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">How we plan to simplify that with Lift.</li></ul><h1 id="a926" class="pv pw hv be px py pz qa qb qc qd qe qf qg qh qi qj qk ql qm qn qo qp qq qr qs bj">The naive approach</h1><figure class="oy oz pa pb pc pd ov ow paragraph-image"><div role="button" tabindex="0" class="pe pf dj pg bg ph"><div class="ov ow qt"><picture></picture></div></div></figure><p id="b6a6" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">When going serverless, SQS + AWS Lambda is an iconic duo:</p><ul class=""><li id="b6ad" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">SQS is a queue service that is entirely managed by AWS, scales automatically, and bills by the usage.</li><li id="02eb" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">AWS Lambda runs our code and is entirely managed by AWS, scales automatically, and bills by the usage.</li></ul><p id="25d8" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">The best part: Lambda integrates with SQS natively. We can write “queue workers” on Lambda without having to poll messages from SQS: instead, <strong class="nx hw">our code (the worker) is automatically invoked when a new message (aka job) is pushed into SQS</strong>.</p><p id="f7f2" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">To deploy queues and workers with the Serverless Framework, we need a bit of CloudFormation:</p><pre class="oy oz pa pb pc qu qv qw qx ax qy bj"># serverless.yml# ...functions:    worker:        handler: worker.handler        events:            # Subscribe our function to the SQS queue            -   sqs:                    arn: !GetAtt ReportGenerationQueue.Arnresources:    Resources:<em class="ot"># This creates an SQS queue</em>ReportGenerationQueue:            Type: AWS::SQS::Queue</pre><p id="05a3" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">So far so good! Or at least, it’s doable.</p><h2 id="1d4d" class="qz pw hv be px re rf do qb rg rh dq qf og ri rj rk ok rl rm rn oo ro rp rq rr bj">Problems with the naive approach</h2><p id="3e21" class="pw-post-body-paragraph nv nw hv nx b ny rs oa ob oc rt oe of og ru oi oj ok rv om on oo rw oq or os ho bj">The biggest missing piece here is error handling.</p><p id="bfed" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj"><strong class="nx hw">By default, SQS retries failed jobs indefinitely</strong>. Imagine your code has a bug (let’s pretend that can happen): the job will be retried over and over for days, possibly costing a lot of money and wasting resources.</p><p id="3b91" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">To deal with errors, we need to set up a “<em class="ot">dead letter queue</em>” (a queue that stores failed messages) and limit the number of retries.</p><p id="426d" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">On top of that, there are many details that needs to be configured, for example:</p><ul class=""><li id="f457" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">We should configure the dead letter queue to store failed messages for 14 days (the maximum).</li><li id="61b0" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">We should set up an <strong class="nx hw">alarm</strong> that sends our team an email whenever there are failed messages in the dead letter queue.</li><li id="cfd7" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">Many other settings need to be fine-tuned: the SQS “visibility timeout”, the Lambda max concurrency, message batching, etc.</li></ul><h1 id="8661" class="pv pw hv be px py pz qa qb qc qd qe qf qg qh qi qj qk ql qm qn qo qp qq qr qs bj">A production-ready approach</h1><figure class="oy oz pa pb pc pd ov ow paragraph-image"><div role="button" tabindex="0" class="pe pf dj pg bg ph"><div class="ov ow rx"><picture></picture></div></div></figure><p id="8ea4" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">Here is a preview of a <code class="ff ry rz sa qv b">serverless.yml</code> configuration that includes those best practices:</p><pre class="oy oz pa pb pc qu qv qw qx ax qy bj"># serverless.yml# ...functions:    worker:        handler: worker.handler        timeout: 10 # seconds        # Scale to maximum 10 worker instances        reservedConcurrency: 10        events:            -   sqs:                    arn: !GetAtt ReportGenerationQueue.Arn<em class="ot"># Process messages without batching</em>batchSize: 1resources:    Resources:<em class="ot"># Create an SQS queue</em>ReportGenerationQueue:            Type: AWS::SQS::Queue            Properties:<em class="ot"></em>QueueName: !Sub '${AWS::StackName}-myqueue'<em class="ot"># 6 times the lambda function's timeout</em>VisibilityTimeout: 60<em class="ot"># Messages will be stored (and retried) up to 2 days</em>MessageRetentionPeriod: 172800                RedrivePolicy:<em class="ot"># Jobs will be retried 5 times</em>maxReceiveCount: 5<em class="ot"># Send failed messages to the dead letter queue</em>deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn<em class="ot"># Failed messages will be sent to this queue</em>DeadLetterQueue:            Type: AWS::SQS::Queue            Properties:<em class="ot"># Store messages up to 14 days (the max)</em><em class="ot"># we want time to debug/analyse</em>MessageRetentionPeriod: 1209600</pre><p id="a7d1" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">If we wanted to add an alarm for failed messages, we would need 30 more lines of YAML.</p><p id="59e3" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">Needless to say:</p><ul class=""><li id="919b" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">this is hard to figure out,</li><li id="3397" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">this is tedious to implement.</li></ul><p id="1fba" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">We want to make that simpler with Lift.</p><h1 id="4cdb" class="pv pw hv be px py pz qa qb qc qd qe qf qg qh qi qj qk ql qm qn qo qp qq qr qs bj">Serverless queues and workers with Lift</h1><p id="0e59" class="pw-post-body-paragraph nv nw hv nx b ny rs oa ob oc rt oe of og ru oi oj ok rv om on oo rw oq or os ho bj">We are currently working on a “Queues” component that can be deployed via <code class="ff ry rz sa qv b">serverless.yml</code>:</p><pre class="oy oz pa pb pc qu qv qw qx ax qy bj"># serverless.yml# ...queues:  report-generation:    # Our Lambda worker function is defined in the "queue" component:    worker:      handler: worker.handler      # Scale to maximum 10 worker instances      reservedConcurrency: 10</pre><p id="58a8" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">As we can see in the example above, there is a new <code class="ff ry rz sa qv b">queues</code> section that lets us define queues (and their workers). In that instance, we define a <code class="ff ry rz sa qv b">report-generation</code> queue and its worker function.</p><p id="cbb6" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">On <code class="ff ry rz sa qv b">serverless deploy</code> , Lift will create:</p><ul class=""><li id="a8f8" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">A <code class="ff ry rz sa qv b">report-generation</code> SQS queue configured following best practices.</li><li id="854e" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">A <code class="ff ry rz sa qv b">worker</code> Lambda function, that will be automatically subscribed to the SQS queue.</li><li id="7287" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">A <code class="ff ry rz sa qv b">report-generation-dlq</code> SQS “dead letter queue” that will receive failed messages.</li></ul><p id="33d3" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">By default, messages are configured to be retried up to 3 times.</p><p id="273e" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">We are considering several settings on the component, including setting an email alert on failed messages:</p><pre class="oy oz pa pb pc qu qv qw qx ax qy bj"># serverless.yml# ...queues:  report-generation:    alarm: alerting@my-company.com    worker:...</pre><p id="7e9d" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">On top of that, Lift also simplifies <strong class="nx hw">pushing messages</strong> into the SQS queue:</p><figure class="oy oz pa pb pc pd ov ow paragraph-image"><div role="button" tabindex="0" class="pe pf dj pg bg ph"><div class="ov ow sb"><picture></picture></div></div></figure><ul class=""><li id="ff38" class="nv nw hv nx b ny nz oa ob oc od oe of pk oh oi oj pl ol om on pm op oq or os pn po pp bj">Lambda functions defined in <code class="ff ry rz sa qv b">serverless.yml</code> will automatically get <strong class="nx hw">IAM permissions</strong> to send messages to the SQS queue.</li><li id="cd9f" class="nv nw hv nx b ny pq oa ob oc pr oe of pk ps oi oj pl pt om on pm pu oq or os pn po pp bj">To simplify referencing the SQS queue, Lift introduces <strong class="nx hw">a new variable syntax</strong>: <code class="ff ry rz sa qv b">${queues:my-queue.queueUrl}</code></li></ul><p id="d268" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">Here is a complete example with a <em class="ot">publisher</em> Lambda function:</p><pre class="oy oz pa pb pc qu qv qw qx ax qy bj"># serverless.yml# ...functions:  publisher:    handler: publisher.handler    environment:      QUEUE_URL: ${queues:report-generation.queueUrl}queues:  report-generation:    worker:      handler: worker.handler</pre></div></div></div><div class="ab cm sc sd se sf" role="separator"><div class="ho hp hq hr hs"><div class="ab cm"><div class="fg fh fi fj fk fl fm bg"><p id="110e" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">Deploying queues, <a class="af ou" rel="noopener" href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b">static websites</a> and <a class="af ou" rel="noopener" href="https://medium.com/serverless-transformation/file-storage-on-aws-designing-lift-1caf8c7b9bb0">file storage</a> is a small part of what we are working on with Lift.</p><p id="ea4a" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">To follow Lift’s development, <a class="af ou" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank"><strong class="nx hw">star or watch Lift on GitHub</strong></a>.</p><p id="8ff6" class="pw-post-body-paragraph nv nw hv nx b ny nz oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ho bj">We are also looking for feedback on the <em class="ot">Queues</em> feature: <a class="af ou" href="https://github.com/getlift/lift/discussions/12" rel="noopener ugc nofollow" target="_blank"><strong class="nx hw">get involved in the GitHub discussion</strong></a><strong class="nx hw">.</strong></p></div></div></div></div></div></div></div></div></div></section></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="xm xn xo cy dc"><img alt="Static websites on AWS — Designing Lift" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*C319QHXsbb1UosEPTe3x_g.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@mnapoli?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="l dj"><img alt="Matthieu Napoli" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*AsvIQHCqPf_B-uaal5exEA.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@mnapoli?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Matthieu Napoli</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/serverless-transformation?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Serverless Transformation</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Static websites on AWS — Designing Lift</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">This article is part of a series on Lift, an open-source project that simplifies deploying serverless applications.</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="ab q">3 min read·Apr 16, 2021</div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fserverless-transformation%2F1db94574ba3b&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fstatic-websites-on-aws-designing-lift-1db94574ba3b&amp;user=Matthieu+Napoli&amp;userId=f99d87d77f4&amp;source=-----1db94574ba3b----0-----------------clap_footer----e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b?source=author_recirc-----d870afdba867----0---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/serverless-transformation/asynchronous-client-interaction-in-aws-serverless-polling-websocket-server-sent-events-or-acf10167cc67?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="xm xn xo cy dc"><img alt="Asynchronous client interaction in AWS Serverless: Polling, WebSocket, Server-Sent Events or…" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*n-asxYQNDkIDWOUYjd1dRQ.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="l dj"><img alt="Xavier Lefèvre" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*O3nnPSB8S7FbXUAiUza2qg.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Xavier Lefèvre</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/serverless-transformation?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Serverless Transformation</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/serverless-transformation/asynchronous-client-interaction-in-aws-serverless-polling-websocket-server-sent-events-or-acf10167cc67?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div title="Asynchronous client interaction in AWS Serverless: Polling, WebSocket, Server-Sent Events or…"><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Asynchronous client interaction in AWS Serverless: Polling, WebSocket, Server-Sent Events or…</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">Event-driven Serverless often requires async update to the client. There are several ways to achieve this, but which is best? And how?</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/serverless-transformation/asynchronous-client-interaction-in-aws-serverless-polling-websocket-server-sent-events-or-acf10167cc67?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="ab q">6 min read·Apr 18, 2020</div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fserverless-transformation%2Facf10167cc67&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fasynchronous-client-interaction-in-aws-serverless-polling-websocket-server-sent-events-or-acf10167cc67&amp;user=Xavier+Lef%C3%A8vre&amp;userId=3f1ab874c1ca&amp;source=-----acf10167cc67----1-----------------clap_footer----e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/serverless-transformation/asynchronous-client-interaction-in-aws-serverless-polling-websocket-server-sent-events-or-acf10167cc67?source=author_recirc-----d870afdba867----1---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/serverless-transformation/what-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="xm xn xo cy dc"><img alt="What a typical 100% Serverless Architecture looks like in AWS!" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*JxDFqhb95iPclzr2FJcQAQ.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="l dj"><img alt="Xavier Lefèvre" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*O3nnPSB8S7FbXUAiUza2qg.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Xavier Lefèvre</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/serverless-transformation?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Serverless Transformation</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/serverless-transformation/what-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">What a typical 100% Serverless Architecture looks like in AWS!</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">If you are new to serverless and looking for a high level web architecture guide, you’ve come to the right place!</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/serverless-transformation/what-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="ab q">11 min read·May 19, 2020</div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fserverless-transformation%2F40f252cd0ecb&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fwhat-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb&amp;user=Xavier+Lef%C3%A8vre&amp;userId=3f1ab874c1ca&amp;source=-----40f252cd0ecb----2-----------------clap_footer----e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/serverless-transformation/what-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb?source=author_recirc-----d870afdba867----2---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/serverless-transformation/is-serverless-cheaper-for-your-use-case-find-out-with-this-calculator-2f8a52fc6a68?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="xm xn xo cy dc"><img alt="Is serverless cheaper for your use case? Find out with this calculator." class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*obcUBXpocpGePgYWeLeJZA.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="l dj"><img alt="Xavier Lefèvre" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*O3nnPSB8S7FbXUAiUza2qg.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@xavierlefevre?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Xavier Lefèvre</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/serverless-transformation?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Serverless Transformation</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/serverless-transformation/is-serverless-cheaper-for-your-use-case-find-out-with-this-calculator-2f8a52fc6a68?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div title="Is serverless cheaper for your use case? Find out with this calculator."><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Is serverless cheaper for your use case? Find out with this calculator.</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">Some fixed architectural opinions simplify the process of estimating a serverless project cost!</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/serverless-transformation/is-serverless-cheaper-for-your-use-case-find-out-with-this-calculator-2f8a52fc6a68?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="ab q">10 min read·Jul 30, 2020</div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fserverless-transformation%2F2f8a52fc6a68&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fserverless-transformation%2Fis-serverless-cheaper-for-your-use-case-find-out-with-this-calculator-2f8a52fc6a68&amp;user=Xavier+Lef%C3%A8vre&amp;userId=3f1ab874c1ca&amp;source=-----2f8a52fc6a68----3-----------------clap_footer----e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/serverless-transformation/is-serverless-cheaper-for-your-use-case-find-out-with-this-calculator-2f8a52fc6a68?source=author_recirc-----d870afdba867----3---------------------e82f6ef0_8cf5_41c4_9296_ff49dc4814ff-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/artificial-corner/youre-using-chatgpt-wrong-here-s-how-to-be-ahead-of-99-of-chatgpt-users-886a50dabc54?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="You’re Using ChatGPT Wrong! Here’s How to Be Ahead of 99% of ChatGPT Users" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*y0vJwEfN45barnQO9jiYew.jpeg" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@frank-andrade?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="The PyCoach" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*veEX4-CiLz5jqUjwWfQo_Q.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@frank-andrade?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">The PyCoach</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/artificial-corner?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Artificial Corner</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/artificial-corner/youre-using-chatgpt-wrong-here-s-how-to-be-ahead-of-99-of-chatgpt-users-886a50dabc54?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title="You’re Using ChatGPT Wrong! Here’s How to Be Ahead of 99% of ChatGPT Users"><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">You’re Using ChatGPT Wrong! Here’s How to Be Ahead of 99% of ChatGPT Users</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">Master ChatGPT by learning prompt engineering.</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/artificial-corner/youre-using-chatgpt-wrong-here-s-how-to-be-ahead-of-99-of-chatgpt-users-886a50dabc54?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·7 min read·Mar 17</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fartificial-corner%2F886a50dabc54&amp;operation=register&amp;redirect=https%3A%2F%2Fartificialcorner.com%2Fyoure-using-chatgpt-wrong-here-s-how-to-be-ahead-of-99-of-chatgpt-users-886a50dabc54&amp;user=The+PyCoach&amp;userId=fb44e21903f3&amp;source=-----886a50dabc54----0-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/artificial-corner/youre-using-chatgpt-wrong-here-s-how-to-be-ahead-of-99-of-chatgpt-users-886a50dabc54?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/javarevisited/10-microservices-design-principles-every-developer-should-know-44f2f69e960f?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="Top 10 Microservices Design Principles and Best Practices for Experienced Developers" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*rk7J-JBE6KDRGaG3qWvfEQ.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@somasharma_81597?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="Soma" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*FF1NpqC-cd41RUh0wv7ZDw.png" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@somasharma_81597?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Soma</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/javarevisited?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Javarevisited</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/javarevisited/10-microservices-design-principles-every-developer-should-know-44f2f69e960f?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title="Top 10 Microservices Design Principles and Best Practices for Experienced Developers"><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Top 10 Microservices Design Principles and Best Practices for Experienced Developers</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">Designing Microservices for your organization? Follow these design principle to create a robust and scalable Microservices</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/javarevisited/10-microservices-design-principles-every-developer-should-know-44f2f69e960f?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·10 min read·Feb 11</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fjavarevisited%2F44f2f69e960f&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fjavarevisited%2F10-microservices-design-principles-every-developer-should-know-44f2f69e960f&amp;user=Soma&amp;userId=69b3d76d273f&amp;source=-----44f2f69e960f----1-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/javarevisited/10-microservices-design-principles-every-developer-should-know-44f2f69e960f?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/better-programming/are-we-making-lambda-too-hard-4008572ac63a?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="Are We Making Lambda Too Hard?" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/0*lE3BgqRDj6iwbIEP.jpg" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@allenheltondev?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="Allen Helton" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*ifG4msqzs8x1wQxrsjbIqQ.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@allenheltondev?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Allen Helton</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/better-programming?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Better Programming</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/better-programming/are-we-making-lambda-too-hard-4008572ac63a?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Are We Making Lambda Too Hard?</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">Hot take alert! Some of the “best practices” we’re seeing nowadays are taking the simplicity of Lambda and throwing it right out the door.</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/better-programming/are-we-making-lambda-too-hard-4008572ac63a?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·8 min read·4 days ago</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fbetter-programming%2F4008572ac63a&amp;operation=register&amp;redirect=https%3A%2F%2Fbetterprogramming.pub%2Fare-we-making-lambda-too-hard-4008572ac63a&amp;user=Allen+Helton&amp;userId=506242edfbaf&amp;source=-----4008572ac63a----0-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/better-programming/are-we-making-lambda-too-hard-4008572ac63a?source=read_next_recirc-----d870afdba867----0---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/geekculture/aws-lambda-limitations-in-real-life-applications-3db7b00b8796?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="AWS Lambda Limitations in Real-Life Applications" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*YG3V8yL0bMkOiyDoYnfkqg.jpeg" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@56faisal?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="Mohammad Faisal" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*-AnRurxYM1u0PRMIR60Oyg.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@56faisal?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Mohammad Faisal</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/geekculture?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Geek Culture</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/geekculture/aws-lambda-limitations-in-real-life-applications-3db7b00b8796?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">AWS Lambda Limitations in Real-Life Applications</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">How to workaround the limitations of AWS Lambda</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/geekculture/aws-lambda-limitations-in-real-life-applications-3db7b00b8796?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·3 min read·Dec 26, 2022</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgeekculture%2F3db7b00b8796&amp;operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2Fgeekculture%2Faws-lambda-limitations-in-real-life-applications-3db7b00b8796&amp;user=Mohammad+Faisal&amp;userId=fe04a352a811&amp;source=-----3db7b00b8796----1-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/geekculture/aws-lambda-limitations-in-real-life-applications-3db7b00b8796?source=read_next_recirc-----d870afdba867----1---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/gitconnected/why-i-keep-failing-candidates-during-google-interviews-dc8f865b2c19?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="Why I Keep Failing Candidates During Google Interviews…" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*FyaF0pPskcOtQ_MmEnBjZA.jpeg" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@alexcancode?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="Alexander Nguyen" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*cwYWYCjbeXNc_pAtTeq_Zg.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@alexcancode?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Alexander Nguyen</p></a></div></div></div><div class="ya l"><p class="be b dm z dl">in</p></div><div class="l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" href="https://medium.com/gitconnected?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------" rel="noopener follow"><p class="be b dm z km kn ko kp kq kr ks kt bj">Level Up Coding</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/gitconnected/why-i-keep-failing-candidates-during-google-interviews-dc8f865b2c19?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Why I Keep Failing Candidates During Google Interviews…</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">They don’t meet the bar.</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/gitconnected/why-i-keep-failing-candidates-during-google-interviews-dc8f865b2c19?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·4 min read·Apr 13</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgitconnected%2Fdc8f865b2c19&amp;operation=register&amp;redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fwhy-i-keep-failing-candidates-during-google-interviews-dc8f865b2c19&amp;user=Alexander+Nguyen&amp;userId=a148fd75c2e9&amp;source=-----dc8f865b2c19----2-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/gitconnected/why-i-keep-failing-candidates-during-google-interviews-dc8f865b2c19?source=read_next_recirc-----d870afdba867----2---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="j i d"></div></div></div></div></div></div></div></article><article class="di"><div class="di tb l"><div class="bg di"><div class="di l"><div class="di wx wy wz xa xb xc xd xe xf xg xh xi xj"><div class="xk"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Image" rel="noopener follow" href="https://medium.com/@thiwankawickramage/concurrency-with-aws-lambda-everything-you-need-to-know-936c4eedcd54?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="xm xn xo cy dc"><img alt="Concurrency Handling with AWS Lambda" class="bg xp xq xr xs" src="https://miro.medium.com/v2/resize:fit:0/1*eHT9BoSal336z56w67BLfg.png" /></div></a></div><div class="xl ab cm fr"><div class="xt xu xv xw xx ab"><div class="sn l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@thiwankawickramage?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="l dj"><img alt="Thiwanka Wickramage" class="l df bx xy xz ff" src="https://miro.medium.com/v2/resize:fill:40:40/1*XMdmPCROcae-7Kw4zJCKuA.jpeg" width="20" height="20" /></div></a></div></div></div><div class="ya l"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar jx ab q" rel="noopener follow" href="https://medium.com/@thiwankawickramage?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><p class="be b dm z km kn ko kp kq kr ks kt bj">Thiwanka Wickramage</p></a></div></div></div></div><div class="yb yc yd ye yf yg yh yi yj yk l ho"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview Title" rel="noopener follow" href="https://medium.com/@thiwankawickramage/concurrency-with-aws-lambda-everything-you-need-to-know-936c4eedcd54?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div title=""><h2 class="be hw py qa yl ym qb qc qe yn yo qf og rj yp yq rk ok rm yr ys rn oo rp yt yu rq km ko kp kr kt bj">Concurrency Handling with AWS Lambda</h2></div><div class="yv l"><h3 class="be b jv z km yw ko kp yx kr kt dl">In this article we are going to talk about how concurrency works with Lambda, understanding concurrency behaviour, what is Reserved…</h3></div></a></div><a class="af ag ah ai aj ak al am an ao ap aq ar as at" aria-label="Post Preview memebership and reading time information" rel="noopener follow" href="https://medium.com/@thiwankawickramage/concurrency-with-aws-lambda-everything-you-need-to-know-936c4eedcd54?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="ab q"><div class="tb ab"><div class="bl" aria-hidden="false"></div>·5 min read·5 days ago</div></div></a><div class="yy yz za zb zc l"><div class="ab fs"><div class="am zd ze zf zg zh zi zj zk zl zm ab q"><div class="ab q cc"><div class="pw-multi-vote-icon dj kl mc md me"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fp%2F936c4eedcd54&amp;operation=register&amp;redirect=https%3A%2F%2Fthiwankawickramage.medium.com%2Fconcurrency-with-aws-lambda-everything-you-need-to-know-936c4eedcd54&amp;user=Thiwanka+Wickramage&amp;userId=6d8246504d46&amp;source=-----936c4eedcd54----3-----------------clap_footer----bc5ab148_c47f_4823_9925_823b3c404ac6-------"><div class="mf ao ev mg mh mi am mj mk ml me"></div></a></div><div class="pw-multi-vote-count l mm mn mo mp mq mr ms"><p class="be b dm z dl">--</p></div></div><div class="zn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@thiwankawickramage/concurrency-with-aws-lambda-everything-you-need-to-know-936c4eedcd54?source=read_next_recirc-----d870afdba867----3---------------------bc5ab148_c47f_4823_9925_823b3c404ac6-------&amp;responsesOpen=true&amp;sortBy=REVERSE_CHRON"><div><div class="bl" aria-hidden="false"></div></div></a></div></div><div class="ab q zp zq"><div><div class="bl" aria-hidden="false"></div></div></div></div></div></div></div></div></div></div></article>]]></summary>
    <link href="https://medium.com/serverless-transformation/serverless-queues-and-workers-designing-lift-d870afdba867"/>
    <updated>2021-04-30T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b</id>
    <title><![CDATA[Static websites on AWS — Designing Lift]]></title>
    <summary><![CDATA[<header class="pw-post-byline-header gl gm gn go gp gq gr gs gt gu l"><div class="ab gv gw"><div class="ab"><div class="fn l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@mnapoli?source=post_page-----1db94574ba3b--------------------------------"><div class="l dj"><img alt="Matthieu Napoli" class="l df bx gx gy ff" src="https://miro.medium.com/v2/resize:fill:96:96/1*AsvIQHCqPf_B-uaal5exEA.jpeg" width="48" height="48" /></div></a></div><div class="l"><div class="pw-author be b gz ha bj"><div class="hb ab q"><div><div class="bl" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="https://medium.com/@mnapoli?source=post_page-----1db94574ba3b--------------------------------"><div class="ab q">Matthieu Napoli</div></a></div></div><div class="hc hd he hf i d"></div></div><div class="ab q hh"><p class="pw-published-date be b bf z dl">Apr 16, 2021</p><div class="hi bl" aria-hidden="true">·</div><div class="pw-reading-time be b bf z dl">3 min read</div></div></div></div><div class="h k w es et q"><div class="hj l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="hj l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="hj l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="hn ab q"><div><div class="bl" aria-hidden="false"></div></div></div></div><div class="hs s u j i d"><div class="fn l"><div><div class="bl" aria-hidden="false"></div></div><div class="hx l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="hx l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="hx l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="l fp"><div><div class="bl" aria-hidden="false"></div></div><div class="bm l"></div></div></div></div></div></div></div></div></div></div></div></div></div></header><section><div><div class="ih ii ij ik il"><div class=""><p id="d8c6" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj"><em class="jv">This article is part of a series on </em><a class="af jw" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank"><em class="jv">Lift</em></a><em class="jv">, an open-source project that simplifies deploying serverless applications.</em></p><figure class="jy jz ka kb gu kc gi gj paragraph-image"><div role="button" tabindex="0" class="kd ke dj kf bg kg"><div class="gi gj jx"><picture></picture></div></div></figure><p id="d0bb" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">Thanks to tooling like the Serverless Framework, deploying serverless apps on AWS is easier than ever. However, some parts are still harder than they should be.</p><p id="6dac" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">This is why we started <a class="af jw" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank">Lift</a>, an open-source project that simplifies deploying serverless apps. Lift will be distributed as <strong class="iz ip">a plugin that adds new features to the Serverless Framework</strong>. The project is currently work in progress, with a first beta planned for May 2021.</p><p id="3183" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">Let’s kick-off the series with a first feature: <strong class="iz ip">deploying static websites</strong>, like React, Vue or Angular applications:</p><ul class=""><li id="8562" class="kj kk io iz b ja jb je jf ji kl jm km jq kn ju ko kp kq kr bj">How to deploy production-ready static websites on AWS today,</li><li id="4f74" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju ko kp kq kr bj">How we plan to simplify that with Lift.</li></ul><h1 id="a8dc" class="ku kv io be kw kx ky kz la lb lc ld le lf lg lh li lj lk ll lm ln lo lp lq lr bj">The naive approach</h1><p id="2988" class="pw-post-body-paragraph ix iy io iz b ja ls jc jd je lt jg jh ji lu jk jl jm lv jo jp jq lw js jt ju ih bj">To serve static files without managing servers, we can use Amazon S3.</p><p id="1900" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">To do this, we create a S3 bucket, make it public, enable “Website hosting”, and upload our website in there. We can also set up a custom domain with a few extra steps.</p><figure class="jy jz ka kb gu kc gi gj paragraph-image"><div role="button" tabindex="0" class="kd ke dj kf bg kg"><div class="gi gj lx"><picture></picture></div></div></figure><h2 id="dd23" class="ly kv io be kw lz ma do la mb mc dq le ji md me mf jm mg mh mi jq mj mk ml mm bj">Problems with the naive approach</h2><ol class=""><li id="f22c" class="kj kk io iz b ja ls je lt ji mn jm mo jq mp ju mq kp kq kr bj">S3’s “Website hosting” feature <strong class="iz ip">does not support HTTPS</strong>.</li></ol><p id="39e1" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">In 2021, HTTPS is a must-have: that’s a deal breaker.</p><p id="3600" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">2. <strong class="iz ip">No CDN</strong>.</p><p id="ab0e" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">While not a hard requirement for everyone, most alternatives (Netlify, Vercel…) automatically provide a CDN with static websites hosting. This allows caching all over the globe, which can drastically improve performances.</p><h1 id="cd04" class="ku kv io be kw kx ky kz la lb lc ld le lf lg lh li lj lk ll lm ln lo lp lq lr bj">A production-ready approach</h1><p id="ed9a" class="pw-post-body-paragraph ix iy io iz b ja ls jc jd je lt jg jh ji lu jk jl jm lv jo jp jq lw js jt ju ih bj">Amazon CloudFront can solve both our problems:</p><ol class=""><li id="67a7" class="kj kk io iz b ja jb je jf ji kl jm km jq kn ju mq kp kq kr bj"><strong class="iz ip">It supports HTTP and HTTPS</strong>.</li><li id="a1b9" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju mq kp kq kr bj"><strong class="iz ip">It’s a CDN</strong>: it can cache our websites all over the world.</li></ol><figure class="jy jz ka kb gu kc gi gj paragraph-image"><div role="button" tabindex="0" class="kd ke dj kf bg kg"><div class="gi gj mr"><picture></picture></div></div></figure><p id="0f7b" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">While the architecture looks simple on the surface, there are many details to get right:</p><ul class=""><li id="0281" class="kj kk io iz b ja jb je jf ji kl jm km jq kn ju ko kp kq kr bj">Instead of making the S3 bucket public, we should setup “<em class="jv">Origin Access Identity</em>” to allow CloudFront to read in S3.</li><li id="5f51" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju ko kp kq kr bj">To support Single Page Applications like React/VueJS, CloudFront must be configured to serve <code class="ff ms mt mu mv b">index.html</code> when the requested URL does not match in file in S3.</li><li id="4528" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju ko kp kq kr bj">Options like “HTTP 2 support”, “Gzip compression” and “HTTP to HTTPS redirections” should be enabled.</li><li id="fc2f" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju ko kp kq kr bj">Caching should be optimized for static content: with a (sane) default TTL, ignoring cookies and query strings.</li><li id="af2a" class="kj kk io iz b ja ks je kt ji it jm iu jq iv ju ko kp kq kr bj">On deployment, the CloudFront cache must be invalidated to make sure all visitors get the new version of the website.</li></ul><p id="3fda" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">To automate this with CloudFormation, we would need over <strong class="iz ip">70 lines of YAML</strong>… All that for a static website!</p><p id="9138" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">This should be simpler!</p><h1 id="b80f" class="ku kv io be kw kx ky kz la lb lc ld le lf lg lh li lj lk ll lm ln lo lp lq lr bj">Deploying static websites with Lift</h1><p id="11a6" class="pw-post-body-paragraph ix iy io iz b ja ls jc jd je lt jg jh ji lu jk jl jm lv jo jp jq lw js jt ju ih bj"><a class="af jw" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank">Lift</a> is a Serverless plugin that simplifies deploying AWS resources and serverless architectures.</p><p id="a72c" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">We are currently working on a “Static Website” component that can be deployed via <code class="ff ms mt mu mv b">serverless.yml</code>:</p><pre class="jy jz ka kb gu mw mv mx my ax mz bj">service: myappprovider:  name: awsfunctions:  # AWS Lambda functions  # [...]static-websites:  landing:    path: landing/build</pre><p id="55b0" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">On <code class="ff ms mt mu mv b">serverless deploy</code>, Lift will create the necessary resources (CloudFront and S3) and configure them for production.</p><p id="7ea0" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">Lift will also upload the content of the <code class="ff ms mt mu mv b">public/landing</code> directory to S3, and purge the CloudFront cache to make sure the new version is visible by everyone.</p></div><div class="ab cm nf ng hs nh" role="separator"><div class="ih ii ij ik il"><p id="255b" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">Deploying static websites is a small part of what we are working on with Lift.</p><p id="96f0" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">To follow Lift’s development, <a class="af jw" href="https://github.com/getlift/lift" rel="noopener ugc nofollow" target="_blank"><strong class="iz ip">star or watch Lift on GitHub</strong></a>.</p><p id="5e44" class="pw-post-body-paragraph ix iy io iz b ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju ih bj">We are also looking for feedback on the <em class="jv">Static Website</em> feature: <a class="af jw" href="https://github.com/getlift/lift/discussions/5" rel="noopener ugc nofollow" target="_blank"><strong class="iz ip">get involved in the GitHub discussion</strong></a><strong class="iz ip">.</strong></p></div></div></div></div></section>]]></summary>
    <link href="https://medium.com/serverless-transformation/static-websites-on-aws-designing-lift-1db94574ba3b"/>
    <updated>2021-04-16T02:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/2020-open-source-funding/</id>
    <title><![CDATA[Open-source funding in 2020]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">21 January 2021 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Back in January 2019, I <a href="https://null.tc/">created the Null company</a>. My goal: help developers work better, through products, training and consulting. My secondary goal: make some of that work open-source.</p><p>Just like <a href="https://mnapoli.fr/2019-open-source-funding/">my financial report for 2019</a>, here's my yearly update for 2020 on <strong>funding open-source work</strong>.</p><h2>Open source funding</h2><p>Of all the revenue earned by <a href="https://null.tc/">Null</a>, here is the breakdown for 2020:</p><p><img src="https://mnapoli.fr/images/posts/2020-open-source-funding.png" alt="" /></p><p>When I compare that with 2019, I am really happy to have grown the "open-source work" and "products" parts:</p><p><img src="https://mnapoli.fr/images/posts/2019-open-source-funding.png" alt="" /></p><h4>Development services</h4><p>Most of the revenue is still related to development services, i.e. freelancing and consulting.</p><h4>Serverless services</h4><p>I have a specific category for <a href="https://null.tc/">serverless-related consulting</a> and training.</p><p>This year I launched <a href="https://bref.sh/#enterprise"><strong>Bref Enterprise</strong></a> and it has been working out quite well.</p><h4>Products</h4><p>In 2020, I launched 2 products:</p><ul><li><a href="https://serverless-visually-explained.com/"><strong>Serverless Visually Explained</strong></a>, an online course for serverless</li>
<li><a href="https://port7777.com/"><strong>7777</strong></a>, a utility to connect easily to AWS RDS databases, built with <a href="https://blog.deleu.dev/">Marco</a></li>
</ul><p>I absolutely loved doing that, and I have more things planned for 2021 ?</p><h4>Open source work</h4><p>Okay, here we are:</p><blockquote>
<p>In 2020, my company earned around 13600€ directly related to open-source work.</p>
</blockquote><p>Last year, it was 2000€. It is still a small share of the total revenue (and consequently, the money I take home every month), but it is definitely growing!</p><p>After accounting for expenses and taxes, it represents about 430€/month net income. That is not close to a real salary, but it is no longer negligible.</p><p>Last year was a bit premature to draw any conclusion. But looking back now, I can say that even with a widely used open-source project and an active online presence, living <em>directly and only</em> from open-source work (sponsoring) as an individual is extremely hard.</p><p>To be clear, I am more than happy with my situation. What I am getting at is more of a "warning" for others who aspire to live of open-source: don't expect to copy <a href="https://evanyou.me/">Evan You</a> or <a href="https://github.com/calebporzio">Caleb Porzio</a> easily.</p><h2>2021 goals</h2><p>I want to continue the current trend and accelerate it:</p><ul><li>continue working on open-source and grow the sponsoring (<a href="https://github.com/sponsors/mnapoli">want to help?</a> ❤️)</li>
<li>continue delivering products and services that make developers awesome at their jobs!</li>
</ul><p>See you next year!</p>]]></summary>
    <link href="https://mnapoli.fr/2020-open-source-funding/"/>
    <updated>2021-01-21T19:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/aws-lambda-php-docker-containers/</id>
    <title><![CDATA[AWS Lambda can now run PHP using Docker Containers]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">1 December 2020 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>AWS Lambda <a href="https://aws.amazon.com/fr/blogs/aws/new-for-aws-lambda-container-image-support/">now supports running Docker containers</a>!</p><p><strong>This is big news for the PHP community</strong>: while <a href="https://bref.sh/">Bref</a> runtimes provide out-of-the-box support for PHP on AWS Lambda, we can now run any container image ?</p><p>Let's check that out!</p><h2>Lambda runtimes vs. containers</h2><p>Here are the different ways to run code on AWS Lambda:</p><ol><li>Using an official Lambda runtime:
<ul><li>runs on Amazon Linux</li>
<li>supports only a few languages, like JavaScript, Java, Python, etc. but <strong>not PHP</strong></li>
</ul></li>
<li>Using a custom Lambda runtime:
<ul><li>runs on Amazon Linux</li>
<li>the custom runtime is imported via a "Lambda layer" (i.e. a zip file unzipped in <code>/opt</code>)</li>
<li>the custom runtime is built upon the official Lambda "Runtime API"</li>
<li>can support any language we want</li>
<li><strong>This is what Bref currently provides</strong>: custom runtimes to run PHP on Lambda</li>
</ul></li>
<li>Using a Docker image (<strong>new</strong>)
<ul><li>runs on the Linux version you want</li>
<li>with the programs and libraries you want</li>
<li>can support any language</li>
<li>must be made compatible with the official Lambda "Runtime API" (like custom runtimes)</li>
</ul></li>
</ol><p>The last point is important: Lambda is still not made for running daemons and web servers, like Apache, Nginx, etc (which doesn't mean we can't <a href="https://bref.sh/docs/runtimes/http.html">run web applications on Lambda</a>).</p><p>This is a good thing: Lambda's execution model is event-driven, making it extremely scalable and cost-efficient. We can use any container image, but we still need to "bridge it" with the Lambda Runtime API.</p><p>The good news is that Bref provides the "runtime client" for PHP.</p><h2>Docker support in Bref</h2><p>One of the perks of being an <a href="https://aws.amazon.com/developer/community/heroes/matthieu-napoli/">AWS Serverless Hero</a> is that I get early access to new features :) I was able to test things beforehand and make sure that Bref was compatible.</p><p>On top of the PHP Lambda runtimes, Bref provides <a href="https://hub.docker.com/u/bref">Docker images</a> that mirror the Lambda environment:</p><ul><li><code>bref/php-80</code></li>
<li><code>bref/php-74</code></li>
<li><code>bref/php-80-fpm</code></li>
<li><code>bref/php-74-fpm</code></li>
<li>etc.</li>
</ul><p>Using those images as a base for your application is the fastest way to get started: the runtime client is already included in these images. That means that they work out of the box on Lambda. Check out this <code>Dockerfile</code> for example:</p><pre class="language-dockerfile">FROM bref/php-80
# Copy our code in the container
COPY . /var/task
# Start Bref's runtime client
CMD _HANDLER=function.php /opt/bootstrap</pre><p><code>/opt/bootstrap</code> is a file provided by Bref that takes care of invoking your code in the "Lambda way".</p><p>In the coming weeks, we will explore the possibility of using <em>any</em> Docker image, including the official PHP Docker images.</p><h2>What about performances/execution time/deployment size…?</h2><p>The 15-minutes maximum execution time still applies with Docker containers.</p><p>According to AWS, performances should be similar to native runtimes. In my tests, warm invocations were as fast as usual. Cold starts on small Docker images were as fast as native runtimes. I've seen slower cold starts (1-2 seconds) on larger images. We'll be aggregating feedback over the next weeks because it's definitely too early to tell.</p><p>The good news is that while Lambda functions are limited to 250MB, containers can be up to 10GB. That will certainly help when deploying large monoliths to Lambda.</p><p>One limitation to keep in mind is that after 14 days of inactivity, a container-based Lambda function will switch to an "INACTIVE" state. In that state, it takes a few seconds for the Lambda to re-activate, which causes a very slow cold start. I don't expect this to be a problem on large projects, but keep that in mind.</p><h2>Which one to choose?</h2><p>You may have understood now that "Container support" in Lambda mostly means that you can use Docker to package your application.</p><p>It does not involve radical architecture changes for us; our code runs the same way in Docker as in native runtimes: in an event-driven way.</p><p>It is a bit early to be definitive about this, but here is my recommendation as of now:</p><ul><li><strong>use Lambda runtimes by default</strong>:
<ul><li>these are simpler to use and well documented</li>
<li>no Dockerfile to create (you don't even need to <em>know</em> Docker)</li>
<li>with Bref, Lambda runtimes are automatically versioned in sync with the Bref version, making it much easier to keep up to date and upgrade</li>
<li>3rd party tools support Lambda runtimes, most of them don't support containers yet</li>
</ul></li>
<li>use Docker if you have a specific reason for that:
<ul><li>you need to deploy an application larger than 250MB</li>
<li>you want control over the Linux image</li>
<li>you want to include an exotic PHP extension</li>
<li>you want to include specific system libraries or programs</li>
</ul></li>
</ul><p>Docker provides more freedom to support more use cases on Lambda. However, as far as I can tell, it is not a de-facto replacement for AWS runtimes.</p><h2>Example: deploying your first PHP container using Bref</h2><p><strong>Warning: this example is advanced.</strong> If you don't know Bref, <a href="https://bref.sh/docs/">get started via its documentation</a>. The example assumes you know about the current Bref runtimes.</p><p>Since the feature is brand new, you will need to set up everything manually. This is <strong>not representative</strong> of how containers will be supported eventually (we need to wait for container support in the Serverless framework). This example is just so that early-adopters can have a bit of fun.</p><h3>Creating the Docker image</h3><p>To deploy a web application using PHP-FPM (<a href="https://bref.sh/docs/runtimes/http.html">the FPM runtime</a>), we can use a <code>Dockerfile</code> like this:</p><pre class="language-dockerfile"># Uses PHP 8.0, feel free to use php-74-fpm if you prefer
FROM bref/php-80-fpm
COPY . /var/task
CMD _HANDLER=index.php /opt/bootstrap</pre><p><code>index.php</code> is the front controller of the application. For the example, let's keep it simple:</p><pre class="language-php">&lt;?php
echo 'Hello world!';</pre><p>To deploy an event-driven function instead (<a href="https://bref.sh/docs/runtimes/function.html">the Function runtime</a>), we can use a <code>Dockerfile</code> like this:</p><pre class="language-dockerfile"># Uses PHP 8.0, feel free to use php-74 if you prefer
FROM bref/php-80
COPY . /var/task
CMD _HANDLER=function.php /opt/bootstrap</pre><p><code>function.php</code> is the function to invoke:</p><pre class="language-php">&lt;?php
return function () {
    return 'Hello world!';
};</pre><p>As you can see, thanks to the Docker images provided by Bref it's extremely simple!</p><h3>Deploying</h3><p>Let's deploy that:</p><ol><li>
<p>Create a Docker image on AWS:</p>
<ul><li>open the <a href="https://console.aws.amazon.com/ecr/home">ECR Console</a></li>
<li>click "Create repository"</li>
<li>set the name of your image (e.g. <code>app</code>) and validate</li>
</ul></li>
<li>
<p>Push your container image to AWS:</p>
<ul><li>in the <a href="https://console.aws.amazon.com/ecr/home">ECR Console</a>, open the "repository" (or image) that you created</li>
<li>click "View push commands"</li>
<li>run the commands that are displayed</li>
</ul></li>
<li>
<p>Create a Lambda function:</p>
<ul><li>open the <a href="https://console.aws.amazon.com/lambda/home#/functions">Lambda Console</a></li>
<li>click "Create function"</li>
<li>select "Container image"</li>
<li>set a function name (e.g. <code>app</code>)</li>
<li>set the container image by clicking "Browse Images"</li>
<li>click "Create function"</li>
</ul></li>
</ol><p>The function is now ready to be invoked.</p><p>If you are setting up a web application (using the "FPM" runtime), you will need to set up API Gateway. The tutorial stops here: as I said it is meant to illustrate what's different with containers and guide advanced users (who know how to set up API Gateway).</p><p>In the next weeks Bref will natively support containers, so all those steps will be irrelevant.</p><h2>Conclusion</h2><p>That's it! If you have any question, ask me at <a href="https://twitter.com/matthieunapoli">@matthieunapoli</a>.</p><p>Don't forget to read <a href="https://aws.amazon.com/fr/blogs/aws/new-for-aws-lambda-container-image-support/">the official announcement from AWS to learn more</a>.</p><p>If you want to get started, <a href="https://bref.sh/">check out Bref</a> and <a href="https://serverless-visually-explained.com/">Serverless Visually Explained</a>.</p><p>If you need professional support, <a href="https://bref.sh/#enterprise">get in touch here</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/aws-lambda-php-docker-containers/"/>
    <updated>2020-12-01T19:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://bref.sh/docs/news/01-bref-1.0.html</id>
    <title><![CDATA[Bref 1.0 is released ?]]></title>
    <summary><![CDATA[<p>Bref started in November 2017, 3 years ago. Back then, running PHP on AWS Lambda was experimental at best.</p><p>Over the years, as the Bref community grew, as AWS features landed, and as contributors worked, creating serverless PHP applications has become a reality. Bref 0.2 has seen 37 releases. Bref 0.5 has 33. In total, we've released 89 versions since the project started.</p><p>Needless to say, <strong>Bref is stable</strong> and has been for a long time.</p><p>Indeed, Bref runs more than 1 billion requests and jobs every month!</p><p>"Serverless PHP" is no longer a niche, and running scalable PHP applications has never been simpler.</p><p>To celebrate, we're finally releasing <strong>Bref 1.0</strong>!</p><h2 id="1-billion-executions-per-month">1 billion executions per month</h2><p>Thanks to <a href="https://bref.sh/docs/runtimes/#bref-ping">Bref ping</a>, we have an anonymous estimate of Bref's usage, i.e. the number of monthly invocations (HTTP requests, worker jobs, etc.) across all users.</p><p>It illustrates clearly that Bref is getting traction and is used in production, at scale:</p><p><img src="https://bref.sh/docs/news/01/executions.png" alt="" /></p><p>Since we passed 1 billion monthly invocations, I want to celebrate with you this fantastic milestone and thank you for being a part of Bref's community.</p><h2 id="bref-10">Bref 1.0</h2><p>Enough with the fun stuff, what's new?</p><ul><li><a href="https://bref.sh/docs/news/01-bref-1.0.html#lighter-runtimes"><strong>Lighter Lambda runtimes</strong></a> by moving some extensions to an extra layer instead of embedding them by default.</li>
<li><a href="https://bref.sh/docs/news/01-bref-1.0.html#amazon-linux-2"><strong>Migration to Amazon Linux 2</strong></a>, which will be required by AWS Lambda at the end of the year.</li>
<li><a href="https://bref.sh/docs/news/01-bref-1.0.html#documentation-improvements"><strong>Huge documentation improvements</strong></a>: a search bar, a reorganization, documentation of <em>typed handlers</em>, updates all over the place, and a better onboarding experience.</li>
<li><a href="https://bref.sh/docs/news/01-bref-1.0.html#run-functions-locally"><strong>New <code>vendor/bin/bref local</code> command</strong></a> to run functions locally.</li>
<li><a href="https://bref.sh/docs/news/01-bref-1.0.html#api-gateway-v2"><strong>New projects will use API Gateway v2</strong></a> because they are simpler, cheaper, and faster.</li>
</ul><p>What did we break? <strong>Nothing major</strong>, the upgrade should be smooth. Here are the details:</p><ul><li><a href="https://bref.sh/docs/news/01-bref-1.0.html#amazon-linux-2">You need to edit <code>serverless.yml</code></a> to switch to <code>provided.al2</code>.</li>
<li>PHP 7.2 is no longer supported. PHP 7.3 still is.</li>
<li>GD, Imagick, Redis, MongoDB extensions are no longer installed by default; you need to include them via <a href="https://github.com/brefphp/extra-php-extensions">Bref extra extensions</a>.</li>
<li>PHP is now compiled without ZTS (thread safety).</li>
<li>Removed the deprecated <code>lambda()</code> function.</li>
<li>Removed deprecated <code>vendor/bin/bref</code> commands:
<ul><li><code>vendor/bin/bref invoke</code> is replaced by <code>serverless invoke</code>.</li>
<li><code>vendor/bin/bref deployment</code> has become useless.</li>
<li><code>vendor/bin/bref bref.dev</code> has become useless.</li>
</ul></li>
</ul><p><a href="https://github.com/shouze">Sébastien Houzé</a> has worked to make the Bref runtimes (AWS Lambda layers) lighter. One solution we went with was to remove built-in extensions that were not commonly used in most applications:</p><ul><li>GD</li>
<li>Imagick</li>
<li>Redis</li>
<li>MongoDB</li>
</ul><p>By removing them, we make your Lambda lighter, leaving more room for your code and improving cold start performances.</p><p>Of course, we don't want to simply <em>remove</em> those extensions. That's why they've been moved to separate layers in the <a href="https://github.com/brefphp/extra-php-extensions">Bref extra extensions</a> repository, carefully maintained by <a href="https://github.com/Nyholm">Tobias Nyholm</a>.</p><p>For example, to install imagick (via <code>serverless.yml</code>):</p><pre class="language-yaml">functions:
    myapp:
        handler: public/index.php
        layers:
            - ${bref:layer.php-74-fpm}
            - ${bref:extra.imagick-php-74}</pre><h2 id="amazon-linux-2">Amazon Linux 2</h2><p>AWS <a href="https://aws.amazon.com/blogs/compute/migrating-aws-lambda-functions-to-al2/">introduced a new base Linux image</a> for Lambda. We need to upgrade our applications to use it.</p><p>Thanks again to <a href="https://github.com/shouze">Sébastien Houzé</a>, Bref 1.0 will work with Amazon Linux 2. Upgrading is as simple as changing this line in <code>serverless.yml</code>:</p><pre class="language-diff">provider:
    name: aws
-    runtime: provided
+    runtime: provided.al2</pre><p>Remember to upgrade to Bref 1.0 first!</p><h2 id="documentation-improvements">Documentation improvements</h2><p>"Documentation improvements" sounds boring, but I'm extremely happy with this for many reasons:</p><ul><li>onboarding new users now makes much more sense,</li>
<li>current users can troubleshoot and improve their knowledge of Bref and serverless,</li>
<li>expert users can now dig in deeper, especially with "typed handlers".</li>
</ul><h3 id="onboarding-for-new-users">Onboarding for new users</h3><p>New users now start with the <em>FPM</em> runtime.</p><p>Don't worry about FaaS and functions… Start by running <strong>PHP as usual</strong> on a cheap and scalable host. Then, <strong>once you've had your first success</strong>, you can look into the "Function runtime" and its power.</p><p>Check out <a href="https://bref.sh/docs/first-steps.html">the "First steps" guide and see how simple it is!</a>.</p><h3 id="clarity-for-current-users">Clarity for current users</h3><p>A common source of errors and confusion was the two runtimes: "FPM" and "Function".</p><p>To solve that, we clarified the wording and the structure of the documentation:</p><ul><li><a href="https://bref.sh/docs/runtimes/http.html"><strong>Bref for web apps</strong></a> lets you run Laravel, Symfony, etc. on Lambda like on any server, using PHP-FPM (aka the "FPM" runtime),</li>
<li><a href="https://bref.sh/docs/runtimes/function.html"><strong>Bref for event-driven functions</strong></a> lets you handle native AWS Lambda events (aka the "Function" runtime).</li>
</ul><p><em>Bref for web apps</em> is the default runtime (see the "onboarding" section above), so if you're not sure: go with this one.</p><p><a href="https://bref.sh/docs/runtimes/"><img src="https://bref.sh/docs/news/01/doc-menu.png" alt="" /></a></p><h3 id="more-for-expert-users">More for expert users</h3><p>Creating event-driven functions to handle Lambda events can be done using anonymous functions:</p><pre class="language-php">return function ($event) {
    return 'Hello ' . $event['name'];
};</pre><p>But for months now, you can also create fully typed handler classes instead. This is now documented in the <a href="https://bref.sh/docs/function/handlers.html"><strong>Typed PHP Lambda handlers</strong></a> page.</p><p>You can now write asynchronous workers to process SQS queues, process uploaded files via S3 events, create decoupled microservices using EventBridge, etc. Well, you already could, but now it will be a bit easier.</p><blockquote>
<p>Self-promotion time: I've helped enterprises refactor their microservice architectures using Lambda and SQS/EventBridge. If you are interested, <a href="mailto:matthieu@null.tc">get in touch to work together</a>. You can also check out <a href="https://serverless-visually-explained.com/?ref=bref-1.0">Serverless Visually Explained</a>, it contains examples for those use cases. &lt;/self-promo&gt;</p>
</blockquote><p>Finally, the <a href="https://bref.sh/docs/environment/performances.html#bref-for-event-driven-functions"><code>BREF_LOOP_MAX</code> variable</a> is now documented, for those ready to keep the PHP process alive between events to accelerate their workers.</p><p>The website has been slightly redesigned:</p><ul><li>a top menu bar has been introduced, to easily navigate between the home, documentation, news and GitHub repository,</li>
<li>a <strong>search field</strong> has been introduced ?</li>
</ul><p>Thanks to <a href="https://docsearch.algolia.com/">Algolia DocSearch</a> for providing the search engine!</p><p>Bref provides Docker images to run <em>web apps</em> locally (e.g. Laravel or Symfony) and that works well.</p><p>However, when creating event-driven functions, running them locally via <code>serverless</code> and Docker was slow and unstable.</p><p>Bref 1.0 introduces a new command:</p><pre class="language-bash">$ vendor/bin/bref local hello
Hello world
# With JSON event data
$ vendor/bin/bref local hello '{"name": "Jane"}'
Hello Jane</pre><p>What's awesome is that you decide if you want to use Docker or not (with the Bref images). You can skip Docker for best performances and ease of use, or use Docker to run in an environment that replicates production.</p><p>Read more in the documentation: <a href="https://bref.sh/docs/function/local-development.html">Local development for functions</a>.</p><h2 id="api-gateway-v2">API Gateway v2</h2><p>API Gateway offers two versions:</p><ul><li>v1's REST API</li>
<li>v2's HTTP API</li>
</ul><p>Bref switches from REST API to HTTP API. Why:</p><ul><li>HTTP API has lower latency (it's slightly faster)</li>
<li>HTTP API <a href="https://aws.amazon.com/blogs/compute/announcing-http-apis-for-amazon-api-gateway/">is 70% cheaper</a></li>
<li>HTTP API is simpler to configure, both in the AWS console and <code>serverless.yml</code></li>
<li>HTTP API <strong>do not have the <code>/dev/</code> prefix in URLs!</strong></li>
</ul><p>The last point was a huge pain in the onboarding experience: links and redirects were broken in Symfony and Laravel. This will no longer be the case ?</p><p>HTTP API offers mostly advantages, which is why Bref's documentation and templates have changed to use the new version.</p><p>To update your existing projects, it's only 2 lines to change in <code>serverless.yml</code>:</p><pre class="language-diff">functions:
    website:
        # ...
        events:
-            - http: 'ANY /'
-            - http: 'ANY /{proxy+}'
+            - httpApi: '*'</pre><p><strong>However don't do this blindly:</strong> this will delete your REST API to create a new HTTP API, which will break custom domains you may have set up. Instead, you can deploy the same Lambda with both v1 and v2, <a href="https://bref.sh/docs/environment/custom-domains.html">set up your domain on v2</a>, and then delete v1:</p><pre class="language-yaml">functions:
    website:
        # ...
        events:
            - http: 'ANY /'
            - http: 'ANY /{proxy+}'
            - httpApi: '*'</pre><h2 id="upgrading-breaking-changes">Upgrading breaking changes</h2><p>The deprecated <code>lambda()</code> function has been removed. If you were defining Lambda handlers with it, remove the call to the function. For example:</p><pre class="language-php">// Before
return lambda(function (array $event) {
    return 'Hello ' . $event['name'];
});
// After
return function (array $event) {
    return 'Hello ' . $event['name'];
};</pre><p>The deprecated <code>$_SERVER['LAMBDA_CONTEXT']</code> has been removed in favor of <code>$_SERVER['LAMBDA_REQUEST_CONTEXT']</code> (same content).</p><h2>Thanks</h2><p>A huge thanks to the <a href="https://github.com/brefphp/bref/graphs/contributors">100 Bref contributors</a>, to the community for supporting the project, and to those sponsoring the development:</p><ul><li><a href="https://null.tc/">Null</a></li>
<li><a href="https://geckoengage.com/">GeckoEngage</a></li>
<li><a href="https://laravel.com/">Laravel</a></li>
<li><a href="https://www.jetbrains.com/">JetBrains</a></li>
<li><a href="https://www.twilio.com/">Twilio</a></li>
</ul><p>and <a href="https://github.com/sponsors/mnapoli#sponsors">many others</a>. Thank you all!</p><h2>That's it!</h2><p>Hope you enjoy it! And stay tuned for the AWS re:Invent conference next week!</p><p>You can also join the community <a href="https://bref.sh/docs/community.html">in Slack</a>, post details about your project in <a href="https://github.com/brefphp/bref/issues/267">Built with Bref</a> or share your experience online.</p><p><a href="https://bref.sh/docs/" class="rounded-md shadow px-8 py-8 border text-center font-bold hover:bg-gray-100">What is Bref and serverless?</a> <a href="https://bref.sh/docs/first-steps.html" class="rounded-md shadow px-8 py-8 border text-center font-bold hover:bg-gray-100">Get started with Bref</a></p><div class="hidden fixed top-0 max-h-screen text-xs text-gray-500 max-w-48 mr-2 c1"><p class="mb-2 uppercase font-semibold tracking-wider">Summary</p></div>]]></summary>
    <link href="https://bref.sh/docs/news/01-bref-1.0.html"/>
    <updated>2020-11-16T01:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/sqs-dead-letter-queue-alarm/</id>
    <title><![CDATA[Email alerts on SQS dead letter queues]]></title>
    <summary><![CDATA[<article><header class="clearfix"><p class="article-info text-muted">7 October 2020 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>When running asynchronous tasks on AWS, it often makes sense to send failed tasks to an SQS "<em>Dead Letter Queue</em>".</p><p>A dead letter queue is simply a standard SQS queue that we create to store those failed tasks.</p><p>All that is great, but what do we do with messages in that special queue? It doesn't make sense to process them again, since we know our code fails at that. <strong>What we want instead is get alerted, so that we can inspect those messages and debug the error.</strong></p><p>It is possible to set up email alerts whenever there are messages in the queue. That is doable via a CloudWatch alarm on the "queue size" metric.</p><p>As soon as the queue is not empty, the alarm triggers and sends an email via SNS.</p><p>It's not easy to find a complete CloudFormation example online, so here it one that I wrote while working on <a href="https://port7777.com/">7777</a>:</p><pre class="language-yaml">AWSTemplateFormatVersion: '2010-09-09'
Resources:
    Queue:
        Type: AWS::SQS::Queue
        Properties:
            RedrivePolicy:
                # Jobs will be retried 3 times
                maxReceiveCount: 3
                # And if they still fail, they'll got to the dead letter queue
                deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
    # Failed jobs from the Queue above will end up in this queue
    # (that's the dead letter queue)
    DeadLetterQueue:
        Type: AWS::SQS::Queue
    DlqAlarm:
        Type: AWS::CloudWatch::Alarm
        Properties:
            AlarmName: My-DLQ
            AlarmDescription: 'There are failed messages in the dead letter queue.'
            Namespace: AWS/SQS
            MetricName: ApproximateNumberOfMessagesVisible
            Dimensions:
                -   Name: QueueName
                    Value: !GetAtt DeadLetterQueue.QueueName
            Statistic: Sum
            Period: 60
            EvaluationPeriods: 1
            Threshold: 0
            ComparisonOperator: GreaterThanThreshold
            AlarmActions:
                - !Ref DlqAlarmEmail
    DlqAlarmEmail:
        Type: AWS::SNS::Topic
        Properties:
            Subscription:
                -   Endpoint: me@example.com
                    Protocol: email</pre></article><section class="text-muted border-top mt-5"><p>Interested to learn more about serverless?<br />Check out my interactive course: <a href="https://serverless-visually-explained.com/?utm_source=mnapoli.fr&amp;utm_medium=article&amp;utm_campaign=course" title="Online serverless course" class="font-weight-bold">Serverless Visually Explained</a>.</p></section>]]></summary>
    <link href="https://mnapoli.fr/sqs-dead-letter-queue-alarm/"/>
    <updated>2020-10-07T15:00:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/2019-open-source-funding/</id>
    <title><![CDATA[Open-source funding in 2019]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">3 March 2020 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Back in January 2019, I <a href="https://mnapoli.fr/bref-is-backed-by-null/">created the Null company</a>. My goal: help developers work better, through products, training and consulting. My secondary goal: make some of that work open-source.</p><p>Last year, I was able to work on <a href="https://bref.sh/">Bref</a> and its documentation, various <a href="https://github.com/mnapoli">open-source projects</a>, articles <a href="https://mnapoli.fr/">in this blog</a>, the <a href="https://serverless-php.news/">serverless PHP newsletter</a>, as well as share through <a href="https://mnapoli.fr/presentations/">conferences, meetups</a> and podcasts (a first for me!).</p><p>That was awesome! Now I want to look back at 2019 and see how I am doing with funding that open-source work.</p><h2>Open source funding</h2><p>Of all the revenue earned by <a href="https://null.tc/">Null</a>, here is the breakdown for 2019:</p><p><img src="https://mnapoli.fr/images/posts/2019-open-source-funding.png" alt="" /></p><h5>Development services</h5><p>Most of the revenue was related to development services, aka freelance work and code audits.</p><h5>Serverless services</h5><p>Next, I have a specific category for serverless-related services. That includes:</p><ul><li>serverless consulting</li>
<li>training (<a href="https://training.bref.sh/">hint hint</a>)</li>
<li>serverless or Bref support</li>
</ul><p>I separate this category from the first because <strong>this is a category I want to grow</strong>. I am happy with this result for the first year, especially since it is growing a lot lately. As of March 2020, I have already surpassed 2019 for this specific category.</p><h5>Open source work</h5><p>Okay, here we are:</p><blockquote>
<p>In 2019, Null earned around 2000€ directly related to open-source work.</p>
</blockquote><p>Needless to say, it is a small share of the total revenue. After taxes and company expenses, I get around 750€ as net income from that.</p><p>That year, I spent around 50 days working on open-source (conference talks not included). I also hired a friend to work on Bref for a total of 10 days. While 2000€ is probably most than most open-source developers, it would not cover 60 days of work.</p><p>That revenue comes from different sources:</p><ul><li><a href="https://tidelift.com/">Tidelift</a></li>
<li>more recently: <a href="https://github.com/sponsors/mnapoli">GitHub sponsors</a></li>
</ul><p>And finally, <a href="https://github.com/brefphp/symfony-messenger-sqs">bref/symfony-messenger-sqs</a> should have been my first open-source package whose development was paid by a company. However that bill was never paid. Ironic that my only unpaid bill is for open-source work ;)</p><p>Obviously, this is a category of revenue that I want to grow as well. In 2020, I can already see the trend improving as I am finding more sponsors. I still expect most of the open-source work to be paid for by other activities (i.e. sponsored by Null).</p><h2>2020 goals</h2><p>2020 started pretty strong: more open-source sponsors, brand new services finding their first clients (?), a growing interest in serverless… The future looks bright!</p><p>As for the goals:</p><ul><li>increase the "serverless services" category, because I love working with it: this is a technology that can transform, for the better, how we work</li>
<li>work even more on open-source, which means finding more sponsors (thanks <a href="https://craftcms.com/">CraftCMS</a> and <a href="https://www.jetbrains.com/">JetBrains</a> for leading the way!) and trying out new formats like <a href="https://calebporzio.com/sponsorware">sponsorware</a>
<ul><li>as a side goal, I aim to sponsor or hire other developers to work on open-source eventually</li>
<li>want to help? your company <a href="https://github.com/sponsors/mnapoli">can become a sponsor in a few clicks</a> ❤️</li>
</ul></li>
<li>create paid products: this is new for me, and it could help a lot to fund the open-source work, stay tuned…</li>
</ul><p>There is so much more to say about open-source funding. But I would rather publish that article as it is than store it somewhere and never finish it.</p><p>See you next year!</p><p>PS: you can read also <a href="https://clue.engineering/2020/2019-sustainability-report">Open-Source Sustainability Report (2019)</a>, a similar article by Christian Lück.</p>]]></summary>
    <link href="https://mnapoli.fr/2019-open-source-funding/"/>
    <updated>2020-03-03T19:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/tolerance-is-a-contract/</id>
    <title><![CDATA[Tolerance is a contract]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">21 November 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>We are a few years in the past. I am sitting in a meeting with a very toxic colleague. As the discussion about recruitment and team organization gets heated, I can't help but lash out something like: "Our team has a culture and values that does not fit mission X."</p><p>He can't contain a snarky laugh. What kind of values?</p><p>As the discussion about recruitment and team organization gets heated, I end up bringing up some of the core values of my team: humility, kindness (as in "bienveillance" in french), tolerance.</p><p>He can't help but lash out with a snarky laugh: "Tolerance,</p><p><a href="https://extranewsfeed.com/tolerance-is-not-a-moral-precept-1af7007d6376">https://extranewsfeed.com/tolerance-is-not-a-moral-precept-1af7007d6376</a></p>]]></summary>
    <link href="https://mnapoli.fr/tolerance-is-a-contract/"/>
    <updated>2019-11-21T19:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-php-faq/</id>
    <title><![CDATA[Serverless PHP: frequently asked questions]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">21 November 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>This article is a compilation of answers to the most common questions I get about serverless PHP applications built with <a href="https://bref.sh/">Bref</a>.</p><h2>How is it serverless when there are still servers?</h2><p>Yes, I do get that question :) There are still servers involved, we just don't manage them. We don't even care about them. Servers are out of the equation, which is why the approach is called <em>serverless</em>.</p><h2>How to run serverless PHP applications locally?</h2><p>For HTTP applications, like websites or APIs, Bref provides Docker images to run the stack locally. The <a href="https://bref.sh/docs/local-development.html#http-applications">Bref documentation provides a <code>docker-compose.yml</code> example</a> that you can paste into your project.</p><p>For PHP functions, you can run them locally <a href="https://bref.sh/docs/local-development.html#php-functions">via the <code>serverless invoke local</code> command</a>.</p><h2>How to handle downtime from cloud providers?</h2><p>Indeed, when running applications on cloud providers like AWS, we are exposed to their downtime if they have a major incident. Those are rare, but they do happen once in a while.</p><p>However, those large cloud providers often have less downtime than if we were running our application ourselves. It is important to keep in mind that zero downtime does not exist. The question is about minimizing risk. Would you, or your team, be able to achieve better uptime than AWS, Google, or Microsoft? And if so, is it worth the cost?</p><h2>How do costs scale in practice? Is Lambda still cheap with higher traffic?</h2><p>Like all things, it depends. Smaller applications are very cheap on Lambda because their costs start at $0 (unlike a traditional server which has a fixed price). Larger applications often have more continuous traffic, so cost savings are less guaranteed. However, there are cases or large websites saving money thanks to AWS Lambda (even when migrating from an autoscaling infrastructure).</p><p>I encourage you to have a look at <a href="https://cost-calculator.bref.sh/">cost-calculator.bref.sh</a>, it will help you get an idea. Be careful though: at a larger scale, using AWS ALB instead of API Gateway is much cheaper, and the calculator doesn't take that into account at the moment.</p><h2>How to deal with extra costs related to DDoS attacks or traffic peaks?</h2><p>AWS Lambda and related services are "pay per use". A traffic peak will result in higher costs.</p><p>There are a few ways to mitigate them. First of all, you can set billing alarms on your AWS account. These alarms monitor how much you spent this month, as well as how much you will spend by the end of the month.</p><p>Secondly, you can protect your applications from DDoS attacks using services like Cloudflare (free) or AWS Shield (expensive).</p><p>Finally, you can limit the cost impact of traffic spikes by setting limits on your application. For example, you can set the maximum number of instances your Lambda is allowed to scale up to. By default, the limit is 1000. By setting it to a lower number, like 5, you can limit the impact of a peak (at the risk of dropping requests). With a concurrency of 5, an attacker flooding a PHP website for 24 hours would cost around $7, leaving plenty of time to take action before reaching hundreds of dollars.</p><p>On top of AWS Lambda's concurrency configuration, API Gateway also has a "throttling" system. That lets you limit the number of requests per second your website or API will accept.</p><p>Everything I said above mostly applies to malicious attackers. Regular traffic spikes will usually not quadruple your AWS bill. For example, let's say you get 10 times the usual traffic on a specific day. That represents a 30% increase in your monthly bill (because all the other days in the month had the regular traffic). When this happens on my blog or on <a href="https://externals.io/">externals.io</a>: the $0.05 monthly bill can turn into $0.08… Nothing major. Of course, on larger websites, a 30% increase can be more expensive: you have to anticipate them in your budget. You can think of it like this: instead of paying $300/month for servers, you go with a serverless architecture that costs $50/month most of the time, and $300/month on very busy months.</p><h2>Conclusion</h2><p>It is interesting to see many questions are related to billing. Moving to variable pricing is intimidating. However, all "going to serverless" experiences I've seen or heard about end up with cost savings. That doesn't mean that <em>all</em> projects will save on costs obviously.</p><p>If you are interested, check out <a href="https://bref.sh/">bref.sh</a>. I can also work with you to see if serverless can be a good fit for your project, <a href="https://null.tc/">check it out</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-php-faq/"/>
    <updated>2019-11-21T19:00:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-case-study-externals/</id>
    <title><![CDATA[From LAMP to serverless: case study of externals.io]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">11 August 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><em>This article is part of a series of case studies of serverless PHP applications built with <a href="https://bref.sh/">Bref</a> on AWS Lambda. You can <a href="https://bref.sh/docs/case-studies.html">read more of those case studies here</a>.</em></p><p>This case study is about migrating the <a href="https://externals.io/">externals.io</a> website to AWS Lambda using <a href="https://bref.sh/">Bref</a>. This is the first time I write about a <strong>serverless PHP website with a MySQL database</strong>. I hope it will interest a few people ;)</p><p>Externals is a read-only interface for PHP's #internals mailing list. This mailing list is where PHP core developers discuss the future of the language.</p><p><a href="https://externals.io/"><img src="https://mnapoli.fr/images/posts/externals/externals.png" alt="" /></a></p><p>The website (which is <a href="https://github.com/mnapoli/externals">on GitHub</a>) has been migrated from a classic cloud hosting to AWS Lambda in July. I have now a little bit of data to show you and draw some lessons.</p><p>Note that I will not be introducing what AWS Lambda or Bref are in this article: you can read <a href="https://bref.sh/docs/">this page</a> to learn more.</p><h2>Stack</h2><p>The application is a traditional LAMP stack (Linux, Apache, MySQL, PHP) with the addition of a cron. The cron fetches the last emails of the mailing list and saves them to the database. The application used to run on Platfom.sh via a plan graciously sponsored by the company:</p><p><img src="https://mnapoli.fr/images/posts/externals/stack-before.svg" alt="" /></p><p>The new architecture is very similar. The main difference is that instead of running on one server with traditional services, the application now runs using multiple AWS services:</p><p><img src="https://mnapoli.fr/images/posts/externals/stack-after.svg" alt="" /></p><p>As you can see, while some AWS services have been added to the mix, nothing major has changed. And if you have read the <a href="https://bref.sh/">Bref</a> documentation at some point, the schema may look familiar: this architecture is documented in the <a href="https://bref.sh/docs/websites.html">"Serverless Websites" documentation on bref.sh</a>.</p><p>To be honest, the main complexity in the stack is related to CloudFront. CloudFront is the AWS CDN, and it helps us serve assets (CSS, JS) from S3. It works well, but setting it up requires around 60 lines of YAML boilerplate. There is definitely room for improvement, and <a href="https://github.com/serverless/components">I have a few ideas in mind</a>.</p><p>In the last section of this article, I detail <strong>everything I had to change in the code</strong> for the migration. But before that, let's talk performances and pricing.</p><h2>Performances</h2><p>Here is the HTTP response time from API Gateway:</p><p><img src="https://mnapoli.fr/images/posts/externals/performance-graph.png" alt="" /></p><p>As you can see, <strong>the median response time is 55ms</strong>. Pretty good for a website!</p><p>Note: this is the full HTTP response time of API Gateway, not just the Lambda execution time (PHP execution time). PHP's execution time is 15-20ms <em>less</em> than API Gateway's response time, i.e. 35ms on average.</p><p>What about <strong>cold starts</strong> and slow pages? Here are more numbers:</p><p><img src="https://mnapoli.fr/images/posts/externals/performance-numbers.png" alt="" /></p><ul><li>90% of the requests are below 100ms</li>
<li>1% of the requests are over 500ms</li>
<li>0.5% of all requests are cold starts</li>
</ul><p>Having 1% of requests response in 500ms to 3s is not ideal. However <strong>it is perfectly acceptable here</strong>. Especially when you consider the full page loading time for users (with assets and JS execution), which is often more than 10 seconds on most sites.</p><h2>Costs</h2><p>Externals was previously hosted on Platform.sh, running on the smallest $50/month plan. Though the plan was offered by Platform.sh to support externals.io, let's keep in mind the official price for a fair comparison.</p><p>Here is the monthly cost for the serverless version on AWS:</p><table><thead><tr><th class="c1">$/month</th>
</tr></thead><tbody><tr><td>AWS Lambda</td>
<td class="c2">$0.37</td>
</tr><tr><td>API Gateway</td>
<td class="c2">$0.71</td>
</tr><tr><td>CloudFront</td>
<td class="c2">$0.47</td>
</tr><tr><td>AWS S3 (assets)</td>
<td class="c2">$0.12</td>
</tr><tr><td>RDS (database)</td>
<td class="c2">$15.70</td>
</tr><tr><td><strong>Total</strong></td>
<td class="c2"><strong>$17.37</strong></td>
</tr></tbody></table><p>(the free tier has been ignored, I actually pay even less than that)</p><p>As we can see, most of the cost comes from the database (which is the smallest available, and doesn't break a sweat). The rest of the website totals to $1.67/month.</p><p>The traffic received each month by the website is:</p><ul><li>5200 visitors (Google Analytics)</li>
<li>100,000 HTTP requests going to PHP</li>
</ul><p>And in case you wonder who pays the bill of externals.io now: <a href="https://null.tc/">null</a>.</p><h3>What about spikes?</h3><p>Something I hear very often regarding the serverless billing system is:</p><blockquote>
<p>What if there's a huge traffic spike? I'll end up paying a lot of money.</p>
</blockquote><p>That's a real question. Let's try to give some numbers. I don't have a groundbreaking traffic spike to illustrate, but I do have a little one:</p><p><img src="https://mnapoli.fr/images/posts/externals/spike.png" alt="" /></p><p>In the last few days, there has been a lot of activity on the site: internal developers are talking about major changes to the language. This has been linked over Twitter, Reddit, Facebook and Hacker News and that brought some traffic.</p><p><img src="https://mnapoli.fr/images/posts/externals/spike-cost.png" alt="" /></p><p>This caused a spike that costs me $0.01 on Lambda (went from 1c per day to 2.2c). Roughly speaking, the total extra cost of that spike should be around $0.04.</p><p>This example doesn't really answer the question of a DDoS (which can be prevented using CloudFlare or AWS WAF) and large scale traffic spikes. But I hope it gives a sense of the scale.</p><h3>How to anticipate costs?</h3><p>This question is a tough one. It's nice saying that the website costs only $17/month, but it's hard jumping forward and hoping for the best.</p><p>Over at <a href="https://bref.sh/">Bref</a> we have built a <a href="https://cost-calculator.bref.sh/">serverless pricing calculator</a>. It is mostly targeted at APIs and websites, and should help you anticipate the prices a little better.</p><p>Now let's play a game and see if it would have guessed the cost correctly for externals.io:</p><p><a href="https://cost-calculator.bref.sh/"><img src="https://mnapoli.fr/images/posts/externals/serverless-cost-calculator.png" alt="" /></a></p><p>The calculator doesn't include the database yet, but what we can note:</p><ul><li>AWS Lambda is off by half: this is because the calculator doesn't take into account the cron job, which actually costs about as much as the website (because fetching the emails is slow)</li>
<li>CloudFront is less expensive in reality (maybe I need to account for automatic assets compression in the calculator, which reduces costs a lot)</li>
<li>the rest seems on par!</li>
</ul><p>I hope the calculator will be useful for you.</p><h2>Migration effort</h2><p>In this section, I get into the details of everything I had to change. I also link to the corresponding Bref documentation for each point.</p><ul><li>Move all the assets (CSS, JS, fonts) in a <code>/assets/</code> subfolder. <small><a href="https://bref.sh/docs/websites.html">ℹ️ <em>bref docs</em></a></small></li>
</ul><p>With Apache or Nginx, we can configure routing to "call PHP unless a file exists, in which case serve the file". That is useful to serve assets like CSS, JS, fonts… We simply put them in a <code>public/</code> directory and make that the web root.</p><p>CloudFront has no "fallback" mechanism. Requests must be routed to PHP or to S3 (assets) based on a URL prefix. I chose to move the CSS, JS and fonts into a <code>/assets/</code> URL prefix so that I had 1 rule to configure instead of 3. What can I say, I'm lazy.</p><pre class="language-diff">-&lt;link rel="stylesheet" href="/css/main.min.css?v={{ version }}"&gt;
+&lt;link rel="stylesheet" href="/assets/css/main.min.css?v={{ version }}"&gt;</pre><p>That being said, I think getting rid of the "fallback" mechanism is a good thing: that has always been source of security issues, and it forced us to put our <code>index.php</code> in a <code>public/</code> subdirectory. Now things are a bit more explicit and secure.</p><p><strong>Effort: low</strong></p><ul><li>Remove Platform.sh specific code for configuration and use environment variables. <small><a href="https://bref.sh/docs/environment/variables">ℹ️ <em>bref docs</em></a></small></li>
</ul><p>Platform.sh populated a special environment variable containing some configuration values. I was able to remove all that and replace the configuration of the application by classic environment variables.</p><p>This was actually a win. I will spare you the diff since it's only code removal.</p><p><strong>Effort: low</strong></p><ul><li>Log to <code>stderr</code> instead of a file. <small><a href="https://bref.sh/docs/environment/logs">ℹ️ <em>bref docs</em></a></small></li>
</ul><p>On AWS Lambda, logs should be sent to <code>/dev/stderr</code> to be collected by AWS. This is very simple <a href="https://bref.sh/docs/environment/logs.html">thanks to the <code>bref/logger</code> PSR-3 logger</a> where <code>$log = new \Bref\Logger\StderrLogger()</code> can replace Monolog.</p><p>I use <a href="http://php-di.org/">PHP-DI</a>, here is what the diff looks like:</p><pre class="language-diff">-    LoggerInterface::class =&gt; create(Monolog\Logger::class)
-        -&gt;constructor('app', get('logger.handlers')),
-    // more Monolog configuration...
+    LoggerInterface::class =&gt; create(Bref\Logger\StderrLogger::class),</pre><p><strong>Effort: low</strong></p><ul><li>Move the Twig cache from <code>var/cache</code> to <code>/tmp/cache</code>. <small><a href="https://bref.sh/docs/environment/storage.html#application-cache">ℹ️ <em>bref docs</em></a></small></li>
</ul><p>The code is mounted as read-only in Lambda. The <code>/tmp</code> directory is the only writable directory, which is where I moved Twig's cache.</p><pre class="language-diff">-    'path.cache' =&gt; __DIR__ . '/../../var/cache',
+    'path.cache' =&gt; '/tmp/cache',</pre><p><strong>Effort: low</strong></p><ul><li>Move the <code>./console sync</code> command into a Lambda function.</li>
</ul><p>I had the <code>./console sync</code> command running as a cron every 15 minutes. I could have ported exactly the same command to AWS Lambda, but I decided to make the command more "in line" with what AWS Lambda is about.</p><p>Indeed, we don't need a CLI framework and its abstractions (I mean the Symfony Console) in a cron. After all, I just want to execute a function. So I extracted the content of my Symfony command and put it <a href="https://bref.sh/docs/runtimes/function.html">in a real Lambda function</a>:</p><pre class="language-php">&lt;?php
$app = require __DIR__ . '/res/bootstrap.php';
$synchronizer = $app-&gt;getContainer()-&gt;get(Externals\EmailSynchronizer::class);
lambda(function () use ($synchronizer) {
  $synchronizer-&gt;synchronize();
});</pre><p><strong>Effort: low</strong></p><ul><li>Generate a version number to bust browser cache.</li>
</ul><p>The CSS is included in the page with a version number:</p><pre class="language-html">&lt;link rel="stylesheet" href="/assets/css/main.min.css?v={{ version }}"&gt;</pre><p>This forces browsers to clear their cache when a new version of the website is deployed. Platform.sh provided a unique version as an environment variable, but I found no such thing on AWS Lambda.</p><p>What I had to do is generate a version number when deploying. I use the timestamp in the deployment script:</p><pre class="language-bash">export EXTERNALS_APP_VERSION=$$(date +%s)
serverless deploy</pre><p>Then use that environment variable in the PHP-DI config:</p><pre class="language-php">-    'version' =&gt; env('PLATFORM_TREE_ID'),
+    'version' =&gt; env('EXTERNALS_APP_VERSION'),</pre><p><strong>Effort: medium</strong></p><p>Finally, I obviously had te rewrite the configuration of the application from the Platform.sh format to <code>serverless.yml</code>. This is the part that required more effort. I took advantage of this to create <a href="https://bref.sh/docs/websites.html">the documentation to create websites on bref.sh</a>, so it should definitely be much easier for you now ;)</p><h2>Conclusion</h2><p>Well this was a long article, I won't bore you with more text! If you want to read more of this, you can find <a href="https://bref.sh/docs/case-studies.html">more case studies here</a>.</p><p>In the end, I am very happy with the result. If you have any question ask them here or ping me <a href="https://twitter.com/matthieunapoli">on Twitter</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-case-study-externals/"/>
    <updated>2019-08-11T06:08:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/bref-is-backed-by-null/</id>
    <title><![CDATA[Bref is backed by null, the company]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">6 June 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><a href="https://bref.sh/">Bref is an open source project that helps build serverless PHP applications</a>. I started the project in October 2017 after discovering AWS Lambda. Over time, the project turned from an experiment into a tool used in production by multiple companies.</p><p>But since the beginning, I knew Bref was not like my other open source projects. Serverless is a movement that is changing our industry. It has good sides and its rough edges, but what I love the most about it is that it simplifies our jobs as developers. Soon everyone will be able to deploy and run applications, no matter their skills.</p><p>Because of that, <strong>Bref is a long term project</strong>. Unlike other open source projects that I maintain in my spare time, the vision I have for Bref requires time and commitment.</p><p>To give you an idea, in 2018 I tracked the equivalent of <strong>26 full days</strong> spent working on Bref (the reality might be more around 40), i.e. 5 full-time weeks. Of course I wasn't paid for that. You can multiply that with your daily rate to get an idea of the amount of money I have invested in Bref in 2018. As of June 2019 I know I have already topped that for 2019.</p><p>This is why in January 2019 I created <a href="https://null.tc/">a company</a>:</p><p><a href="https://null.tc/"><img src="https://mnapoli.fr/images/posts/null.png" alt="" /></a></p><p><small><em>I hope you like the name!</em></small></p><p>Besides doing consulting work on PHP projects and application architecture, <strong>null is backing Bref to make sure it has a future for the next years</strong>.</p><p>At the moment that means that I can afford to work on Bref at least 2 days per week, as I have been doing since the beginning of the year. I am working on finding solutions to expand that and even pay more people to work on the project.</p><p>If you are a company interested to sponsor Bref, feel free to <a href="mailto:matthieu@mnapoli.fr">get in touch</a>.</p><p>For those who have been following closely, one step we will take in the coming weeks is to move Bref from my personal GitHub account to the <a href="https://github.com/brefphp">Bref organization on GitHub</a>.</p><p>Slightly related, and I'd like to finish on that: for many months now Bref has been much more than just my contributions.</p><ul><li><a href="https://github.com/bubba-h57">Bubba</a> from <a href="https://stechstudio.com/">Signature Tech Studio</a> has been a major contributor for the build scripts of the PHP runtimes (and he is also building a very nice <a href="https://github.com/stechstudio/laravel-bref-bridge">Laravel integration for Bref</a>), and his colleague <a href="https://github.com/jszobody">Joseph</a> has been welcoming newcomers and providing support in the <a href="https://bref.sh/docs/community.html">Slack channel</a>.</li>
<li><a href="https://twitter.com/nealio82">Neal Brooks</a> has contributed improvements to the Symfony integration and the documentation, and he has been spreading the word about AWS Lambda and Bref to countless meetups and conferences (the next one being <a href="https://laravellive.uk/">Laravel Live in London</a>).</li>
<li><a href="https://github.com/atrope">Alan Trope</a> working for <a href="https://www.suamusica.com.br/">Sua Música</a> has contributed a lot of improvements for high-performance applications, the latest being ALB support.</li>
<li><a href="https://github.com/hollodotme">Holger Woltersdorf</a> is working on an open source project used by Bref: <a href="https://github.com/hollodotme/fast-cgi-client">https://github.com/hollodotme/fast-cgi-client</a>, and he has been very helpful to integrate his work and improve whatever could be improved.</li>
<li><a href="https://akrabat.com/">Rob Allen</a> has been writing a lot of material to get started and learn about AWS Lambda and PHP, including Bref.</li>
<li>the <a href="https://www.enoptea.fr/">Enoptea</a> team lead by <a href="https://twitter.com/Wysow">Gaultier Boniface</a> has helped Bref from the very beginning ?</li>
</ul><p>There are also <a href="https://github.com/mnapoli/bref/graphs/contributors">many more contributors</a>, thanks to all of them.</p>]]></summary>
    <link href="https://mnapoli.fr/bref-is-backed-by-null/"/>
    <updated>2019-06-06T12:06:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-case-study-prettyci/</id>
    <title><![CDATA[Serverless case study: PrettyCI and Laravel Queues]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">21 March 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><em>This article is part of a series of case studies of serverless PHP applications built with <a href="https://bref.sh/">Bref</a> on AWS Lambda. If you are not familiar with serverless and Bref, I invite you to read <a href="https://mnapoli.fr/serverless-php/"><strong>Serverless and PHP: introducing Bref</strong></a>.</em></p><p>This case study is about <a href="https://prettyci.com/">prettyci.com</a>, a SaaS that provides <strong>continuous integration for PHP coding standards</strong> for GitHub repositories.</p><p><a href="https://prettyci.com/"><img src="https://mnapoli.fr/images/posts/prettyci-intro.png" alt="" /></a></p><p>The idea is that anytime you push a commit to your GitHub repository, PrettyCI will analyze that commit using <a href="https://github.com/squizlabs/PHP_CodeSniffer">PHP CodeSniffer</a> or <a href="https://github.com/FriendsOfPHP/PHP-CS-Fixer">PHP-CS-Fixer</a>. Since PrettyCI integrates in <a href="https://blog.github.com/2018-05-07-introducing-checks-api/">GitHub's checks tab</a> you can see the build result directly in your repository without having to leave your work.</p><h2>Architecture</h2><p>I originally created the project using <a href="https://spark.laravel.com/">Laravel Spark</a>. This is a Laravel project pre-built for creating SaaS applications (and it is pretty awesome by the way).</p><p>While Spark takes care of the website, I had to create:</p><ul><li>an API endpoint to receive GitHub webhooks whenever someone pushes new commits</li>
<li>queue wokers that would analyse each commit with <code>phpcs</code> or <code>php-cs-fixer</code></li>
</ul><p>Fortunately Laravel has <a href="https://laravel.com/docs/5.7/queues">a queue system</a> that is very easy to setup.</p><p><a href="https://mnapoli.fr/images/posts/prettyci-architecture.png"><img src="https://mnapoli.fr/images/posts/prettyci-architecture.png" alt="" /></a></p><p>After deploying the whole thing to a small <a href="https://m.do.co/c/1f59f177416b">DigitalOcean server</a> (❤️) using <a href="https://forge.laravel.com">Laravel Forge</a> (❤️) it was running just fine.</p><h2>Serverless workers</h2><p>Since I was running 4 workers on my server that meant that the system could process at most 4 commits at a time. When the 4 workers were busy, new commits to GitHub would be pending and waiting for a worker to free up.</p><p>This is not great for user experience, and while scaling up is doable (see <a href="https://ohdear.app/blog/how-to-size-scale-your-laravel-queues">this great article by Oh Dear</a> for example) it is a bit more work.</p><p>Indeed, that meant adding more servers, which meant more costs and a much higher maintenance effort. This is were my lazy side kicked in.</p><p>As I was <a href="https://mnapoli.fr/serverless-case-study-returntrue/">working with Lambda for returntrue.win</a> at the time, <strong>I decided to migrate the workers to AWS Lambda using <a href="https://bref.sh/">Bref</a>.</strong></p><p>I did the migration in July 2018 and it has been a complete success in my eyes, running great since then.</p><h3>Challenges</h3><p>The migration itself presented some challenges:</p><ul><li><a href="https://bref.sh/docs/environment/storage.html">the filesystem is read-only on Lambda</a> except for <code>/tmp</code> so I had to change the code a bit to use this directory (to checkout repositories and run the analyses)</li>
<li>while Lambda is a Linux environment, <code>git</code> is not available: I had to compile and upload the <code>git</code> binary in my lambda to be able to use it
<ul><li>Since then it is now possible to include a "git layer" like <a href="https://github.com/lambci/git-lambda-layer">this one</a>, which makes the whole thing much easier</li>
</ul></li>
</ul><h3>Downsides</h3><p>Now that the system is running, I don't see many downsides:</p><ul><li>the initial setup was a bit more effort than using the Laravel Queue system out of the box</li>
<li>some problems are a bit harder to debug as it's impossible to SSH into a Lambda and run some tests (you have to go through the cycle of deploying + executing the lambda, or else run the tests locally)</li>
<li>the <code>/tmp</code> folder <a href="https://docs.aws.amazon.com/lambda/latest/dg/limits.html">is limited to 512MB</a>, which made PrettyCI incompatible with projects that have a <em>huge</em> repository</li>
</ul><h3>Advantages</h3><ul><li><strong>jobs are never queued anymore</strong>: this is the killer thing for me here: as soon as a commit is pushed to GitHub a new environment will be launched to process it.
<ul><li>I often see "Pending build" when using Travis/Circle CI/Gitlab CI because the build is waiting for a free container. With PrettyCI that never happens, which makes the user experience awesome (most pull request statuses are updated under 5 seconds).</li>
</ul></li>
<li>infinite scaling with 0 action from my part</li>
<li><a href="https://aws.amazon.com/lambda/pricing/">pay per use</a>, which is actually cheaper than the cheapest DigitalOcean VPS (I pay only the execution time of the workers)</li>
<li>builds are isolated without me having to deal with containers or virtual machines</li>
<li>high availability as all the execution environment is managed by AWS. I just have to focus on the code.</li>
</ul><p><a href="https://prettyci.com/"><img src="https://mnapoli.fr/images/posts/prettyci-builds.png" alt="" /></a></p><h2>Conclusion</h2><p>As explained in <a href="https://bref.sh/docs/#maturity-matrix">the Bref Maturity Matrix</a>, running workers and jobs is an excellent use case for AWS Lambda, and this works perfectly for PrettyCI.</p><p>The migration is a bit more effort than "simply" using Laravel Queues or other similar libraries, but this is something we are trying to simplify at <a href="https://bref.sh/">Bref</a>.</p><p>If you are interested to learn more about AWS Lambda, you can <a href="https://mnapoli.fr/">check out other articles</a> and subscribe to the <a href="https://serverless-php.news/">serverless PHP newsletter</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-case-study-prettyci/"/>
    <updated>2019-03-21T06:03:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/http-performance-bref-0-3/</id>
    <title><![CDATA[HTTP performances with Bref v0.3]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">10 February 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Back in October I published a <a href="https://mnapoli.fr/serverless-case-study-returntrue/">case study of a serverless website: returntrue.win</a>. This website runs on AWS Lambda using <a href="https://bref.sh/">Bref</a>.</p><p>A new major version of Bref (v0.3) was released this month and it comes with <a href="https://github.com/mnapoli/bref/releases/tag/0.3.0">completely new internals</a>. I was curious, as many others were, of what it meant for performances.</p><p>I benchmarked 2 websites:</p><ul><li>the current <a href="https://returntrue.win/">returntrue.win</a> website running with <strong>Bref 0.2</strong></li>
<li>the same website running with <strong>Bref 0.3</strong></li>
</ul><p>I made sure both lambdas were using the same configuration, which is <strong>1024M</strong> of RAM. For those not aware, the amount of RAM available <a href="https://docs.aws.amazon.com/lambda/latest/dg/resource-model.html">is proportional to the CPU power</a> allocated to the lambda.</p><p>The page that we are testing is running with a custom PHP framework (not optimized, no cache), uses Twig without cache and performs 1 query to DynamoDB.</p><p>It is important to note as well that I am reporting the execution time of the lambda (i.e. the application), not the whole HTTP response time. Since we use API Gateway we need to consider that API Gateway adds about 15ms to lambda's execution times.</p><h2>Cold starts</h2><p><a href="https://hackernoon.com/cold-starts-in-aws-lambda-f9e3432adbf0">Cold starts</a> happen when a new lambda instance is provisioned. By triggering around 20 cold starts for each function I did not see a difference.</p><p>In both cases cold starts where around <strong>650ms</strong>.</p><p>That's a bit disappointing, hopefully we can improve this a bit. I did some tests with 2048M of RAM and cold starts where brought down to 450-500ms.</p><h2>Warm results</h2><p>After testing the cold starts I did performance tests on warm lambdas by running:</p><pre class="language-bash">ab -c 1 -n 500 https://returntrue.win/</pre><table><thead><tr><th class="c1">Average</th>
<th class="c1">Maximum</th>
<th class="c1">Minimum</th>
</tr></thead><tbody><tr><td>Bref v0.2</td>
<td class="c2"><strong>250ms</strong></td>
<td class="c2">320ms</td>
<td class="c2">220ms</td>
</tr><tr><td>Bref v0.3</td>
<td class="c2"><strong>39ms</strong></td>
<td class="c2">106ms</td>
<td class="c2">21ms</td>
</tr></tbody></table><p>This is very good news: the website runs <strong>6 times faster</strong> on average!</p><p>With an average response time of 39ms instead of 250ms, building website or APIs on AWS Lambda becomes a lot more interesting. You can check out below the detailed graph to see how results spread out:</p><p><a href="https://mnapoli.fr/images/posts/http-performance-bref-0-3.png"><img src="https://mnapoli.fr/images/posts/http-performance-bref-0-3.png" alt="" /></a></p><ul><li>red lines: Bref 0.2 (minimum, average, maximum)</li>
<li>green lines: Bref 0.3 (minimum, average, maximum)</li>
</ul><p>I did some tests again with 2048M of memory and results improved even more: 35ms of average response time, with a minimum of 20ms and a maximum of 65ms.</p><h2>Conclusion</h2><p>These results should be taken with a grain of salt as we are testing a small application here. I intend to do more tests in the near future, and I encourage others to do some as well.</p><p>However this is very encouraging! Being able to run PHP applications under 100ms on AWS Lambda is great news as it continues to open more possibilities.</p><p>As a side note I have now deployed <a href="https://returntrue.win/">returntrue.win</a> on Bref v0.3.</p><p>And if you want to give a try to Bref head over to <a href="https://bref.sh/">bref.sh</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/http-performance-bref-0-3/"/>
    <updated>2019-02-10T06:02:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-php-newsletter/</id>
    <title><![CDATA[Launching the Serverless PHP newsletter]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">7 January 2019 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Serverless is a topic that is getting more and more attention lately. While this is exciting, it also means there is a lot to follow, read and test.</p><p>I have met several people who are interested about serverless and its possibilities but have a lot of trouble following everything. This is even more true in the PHP ecosystem because PHP was initially left out from AWS Lambda and other FaaS products.</p><p>In order to help I have started a monthly newsletter called <strong><a href="https://serverless-php.news/">Serverless PHP news</a></strong>.</p><p>This newsletter will contain an overview of what's new regarding serverless and <strong>how it relates to PHP</strong>. Because some news are worth knowing about only if you can relate it to the tools you are using everyday.</p><p>If you are interested, go subscribe at:</p><blockquote>
<p><strong><a href="https://serverless-php.news/">serverless-php.news</a></strong></p>
</blockquote><p>To give you an idea what to expect you can find below a copy of the first email.</p><hr /><p>Welcome to the first issue of the <a href="https://serverless-php.news/">Serverless PHP newsletter</a>.</p><p>First of all I want to wish you a happy new year, and thank you so much. You are more than 400 subscribed to this list and I definitely did not expect that. I hope this newsletter will live up to your expectations and I welcome any feedback (really!).</p><p>This episode is the first so I will sum up <strong>the current state of serverless PHP</strong> and talk about the main event of the last months: AWS re:Invent.</p><p>If you don't know anything about "Serverless" you can read an introduction in <a href="https://martinfowler.com/bliki/Serverless.html">Martin Fowler's serverless bliki entry</a> or <a href="https://mnapoli.fr/serverless-php/">Serverless and PHP: introducing Bref</a>.</p><h3>Before AWS re:Invent</h3><p>AWS re:Invent is a conference held annualy by AWS. This year (November 2018) it changed <em>a lot</em> of things, so let's start by a recap of <em>how PHP ran on AWS Lambda</em> before.</p><p>Since PHP was not officially supported on AWS Lambda the only way to run PHP was to:</p><ul><li>create a JavaScript lambda</li>
<li>compile PHP and include the binary in the lambda</li>
<li>include our PHP script (or application) in the lambda as well</li>
<li>write a JavaScript script that would execute the PHP binary and proxy the HTTP request to PHP</li>
</ul><p>That worked fine but this had an impact on performances. Compiling PHP and writing the JS proxy script was also impractical, which is why <a href="https://github.com/mnapoli/bref">Bref</a> was created in the first place and this is what the current version (v0.2) is about. This is going to change!</p><h3>AWS re:Invent</h3><p>My selection of what was announced in November by AWS that may interest you:</p><ul><li><a href="https://aws.amazon.com/blogs/aws/new-for-aws-lambda-use-any-programming-language-and-share-common-components/">Use <strong>any programming language on AWS Lambda</strong></a> thanks to "lambda layers" and an "open runtime" API (explained in more details in the next section)</li>
<li><a href="https://twitter.com/jeremy_daly/status/1068272580556087296">Cold start improvements when using RDS</a>: RDS is MySQL/PostgreSQL managed by AWS. Using those with Lambda works fine except it requires to put the lambda and the database in the same VPC (private virtual network). This implies a cold start of about 5 to 10 seconds, which is a huge pain for HTTP applications. This will be solved in 2019 which, I believe, is very very good news!</li>
<li><a href="https://aws.amazon.com/about-aws/whats-new/2018/11/aurora-serverless-data-api-beta/">RDS makes MySQL and PostgreSQL accessible via a HTTP API</a>: this is interesting because it allows to skip managing persistent SQL connections and it is much more in line with the serverless paradigm (it is stateless). However the first version released by Amazon <a href="https://www.jeremydaly.com/aurora-serverless-data-api-a-first-look/">is definitely not ready for production right now</a>. AWS is known for releasing alpha services and continuously improving them so let's be patient and keep an eye on it!</li>
<li><a href="https://aws.amazon.com/blogs/aws/firecracker-lightweight-virtualization-for-serverless-computing/">Announcing Firecracker</a>, the "micro-VM" alternative to containers and virtual machines developped by AWS to run AWS Lambda. Their goal: the security of VMs and the low costs (startup time and overhead) of containers. You don't <em>need</em> to learn about this as AWS takes care of the details, but it is interesting to know why they are not using containers.</li>
<li><a href="https://github.com/awslabs/aws-sam-cli">AWS SAM</a> has received a lot of new features which makes it a very serious alternative to <a href="https://serverless.com/">the serverless framework</a> for deployments. AWS SAM is restricted to AWS but comes with very useful development tools running on Docker.</li>
<li><a href="https://aws.amazon.com/blogs/aws/amazon-dynamodb-on-demand-no-capacity-planning-and-pay-per-request-pricing/">DynamoDB goes full serverless</a> via a new "pay per request" pricing: no need to reserve capacity, DynamoDB can now behave just like AWS Lambda: we can pay per request and it will scale on demand automatically.</li>
<li><a href="https://aws.amazon.com/blogs/aws/new-amazon-dynamodb-transactions/">DynamoDB gets transactions support</a> for those interested in replacing their database with DynamoDB. The new API seems limited but it may help in some scenarios.</li>
<li><a href="https://aws.amazon.com/blogs/aws/new-aws-toolkits-for-pycharm-intellij-preview-and-visual-studio-code-preview/">IntelliJ plugin to run and debug serverless applications</a>: this will be interesting for PhpStorm users (the plugin seems to be usable only in IntelliJ for now, let's keep an eye on this)</li>
<li><a href="https://aws.amazon.com/blogs/compute/announcing-websocket-apis-in-amazon-api-gateway/">Websocket support in API Gateway and AWS Lambda</a>: I love how clever this is: instead of having a complex setup with long-running processes holding the connections with the clients (I'm thinking of <a href="https://laravel.com/docs/5.7/broadcasting">Laravel Echo</a> for example) AWS makes it much simpler. API Gateway is responsible for maintaining the websocket connections for you. You then write code that reacts to websocket events (a new connection is made, a new message is received, etc.) via a Lambda function. In other words: back to stateless code, just like in a HTTP context. Check out <a href="https://github.com/aws-samples/simple-websockets-chat-app">this example application built using JavaScript</a>. I am eager to try this in a real-world use case.</li>
<li><a href="https://aws.amazon.com/blogs/networking-and-content-delivery/lambda-functions-as-targets-for-application-load-balancers/">ALB as an alternative to API Gateway for HTTP lambdas</a>. ALB is Amazon's Application Load Balancer and it can now be used to expose Lambdas on the web without having to use API Gateway. ALB is much simpler as there is no routes to define: it proxies everything to the lambda, and it is supposedly faster as well. There is also a difference in pricing: there is a minimum of $22 per month for ALB where API Gateway starts at $0. However for bigger applications ALB is cheaper. If you are writing websites or APIs on Lambda and pay more than $22 per month on API Gateway it may be worth investigating! Remember to share your experience!</li>
</ul><p>All the re:Invent talks are online on Youtube. I can recommend <a href="https://www.youtube.com/watch?v=HaEPXoXVf2k">this talk about DynamoDB</a> which was very enlightening for me about NoSQL in general and DynamoDB itself. I am tempted to try and rewrite <a href="https://externals.io/">externals.io</a> using Lambda and DynamoDB to learn a bit more about it (hopefully in the coming months).</p><h3>PHP on AWS Lambda</h3><p>Now that we have an official way to run PHP on AWS Lambda, let's look at how it works. The idea is that lambdas can now use <strong>layers</strong>. A layer is a bunch of files that will be injected in the lambda when it starts.</p><p>We can add support for PHP in AWS Lambda by creating a layer that injects the <code>php</code> binary in the lambda. But that is not enough: we need a <code>bootstrap</code>.</p><p>The <code>bootstrap</code> is the file that is called when the lambda starts. It is responsible for executing the PHP script whenever there is a new event. What's great is that the <code>bootstrap</code> file can be written in any language, including PHP.</p><p>AWS announced that a company called Stackery worked on a PHP runtime: <a href="https://github.com/stackery/php-lambda-layer">github.com/stackery/php-lambda-layer</a>. However this runtime is definitely not ready for production and I found it very disappointing on several levels (<a href="https://twitter.com/akrabat/status/1080510028531015680">I am not alone to think this</a>).</p><p>With Bref contributors we have <a href="https://github.com/mnapoli/bref-bootstrap-benchmarks">benchmarked several solutions for running PHP on Lambda</a>. Those include running PHP scripts as well as HTTP applications using PHP-FPM or even PHP's built-in webserver. We have identified the best solutions and we have begun porting them in the new version of Bref.</p><h4>Bref v0.3</h4><p>The vision for <a href="https://github.com/mnapoli/bref">Bref</a> has not changed: it is not just about PHP support, it is about <em>empowering everyone to benefit from serverless technologies</em>. We are hard at work on the next version (v0.3) which will provide:</p><ul><li>stable PHP runtimes for PHP functions as well as HTTP and console applications</li>
<li>much better performances thanks to these native runtimes</li>
<li>a completely rewritten and extensive documentation</li>
<li>a revisited deployment process that integrates with AWS tooling</li>
<li>tools for local development based on Docker (thanks to AWS SAM)</li>
<li>support for any PHP application or framework thanks to PHP-FPM and the console runtime</li>
</ul><p>If you are interested you can follow the <a href="https://github.com/mnapoli/bref/pull/113">v0.3 pull request</a> that summarizes all changes.</p><h3>PHP outside AWS</h3><p>PHP support outside of Amazon is not that great. Google Cloud Functions only supports Python and JavaScript while Microsoft Azure Functions has <em>experimental</em> support for PHP.</p><p>Zeit.co <a href="https://zeit.co/docs/v2/deployments/official-builders/php-now-php/">has basic support for PHP</a> but I wouldn't consider it production-ready: it supports only 1 PHP file per application and runs it via <code>go-php</code>, a PHP 7 implementation in Go. Hopefully this will improve in the future.</p><p><a href="https://www.ibm.com/cloud/functions">IBM OpenWhisk</a> is interesting as it can run Docker containers (so it can run PHP applications). Finally an interesting alternative that may be worth exploring is Alibaba Cloud which <a href="https://www.alibabacloud.com/help/doc-detail/89029.htm">supports PHP functions as well as PSR-7 requests for HTTP applications</a>.</p><p>My personal belief is that AWS is years ahead of its competitors right now, especially after the latest announcements. AWS Lambda is also <a href="https://www.usenix.org/conference/atc18/presentation/wang-liang">very stable and reliable compared to Google Functions and Azure Functions</a>. Let's see how things evolve in the next months and years!</p><h3>Conclusion</h3><p>That's it! This first episode is quite packed but a lot has been happening lately. I hope it helps you get a better overview of all this.</p><p>If you are interested in exploring the serverless world for your next big thing I hope that all of this will be useful. If you need someone to help feel free to get in touch.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-php-newsletter/"/>
    <updated>2019-01-07T12:01:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-case-study-returntrue/</id>
    <title><![CDATA[Serverless case study: returntrue.win]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">29 October 2018 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><em>This article is part of a series of case studies of serverless PHP applications built with <a href="https://github.com/mnapoli/bref">Bref</a> on AWS Lambda. If you are not familiar with serverless and Bref, I invite you to read <a href="https://mnapoli.fr/serverless-php/"><strong>Serverless and PHP: introducing Bref</strong></a>.</em></p><p>This case study is about the <a href="https://returntrue.win/">returntrue.win</a> website. This website was my first serious experiment with serverless and <em>Function as a Service</em>.</p><p><a href="https://returntrue.win/">Returntrue.win</a> is a "puzzle game for PHP developers". A piece of PHP code is shown. Users can fill the missing parameters and run the code. The goal is to make the code return <code>true</code>, then the user can move on to another level.</p><p><a href="https://returntrue.win/"><img src="https://mnapoli.fr/images/posts/returntrue-screenshot.png" alt="" /></a></p><p>Not sure how to get the best scores? Check out <a href="https://www.rpkamp.com/2018/02/15/all-answers-to-returntrue.win-with-explanations/">this complete solution</a>.</p><h2>Architecture</h2><p>The website is built using the following services:</p><ul><li>AWS Lambda for running PHP</li>
<li>
<p>API Gateway to expose the lambdas via HTTP</p>
<p>This service is required if you want to make a lambda accessible on the internet as an API or website. <a href="https://github.com/mnapoli/bref">Bref</a> takes care of configuring API Gateway for you.</p>
</li>
<li>DynamoDB to store the best scores</li>
</ul><p>I chose DynamoDB because I wanted to discover that service and because it was cheaper than RDS. At first I wanted to store the data on disk in a JSON file but the distributed approach of lambdas, and the fact that the filesystem is read-only, put a stop to that. In the end I was happy with that choice and it cost me nothing. But again I stored so little in there that its usage isn't really significant.</p><p>There are 2 lambdas:</p><ul><li>the first lambda is the website</li>
<li>the second lambda runs the code submitted by users (I like to call it "eval-as-a-service")</li>
</ul><p>This separation helps ensure that code submitted by users will not affect the website, even if it crashes.</p><p><img src="https://mnapoli.fr/images/posts/returntrue-flow.png" alt="" /></p><p>I soon discovered that building a website with AWS lambda is a bit trickier than building an API. The whole thing is not built to handle assets (CSS, JavaScript…) out of the box. A simple solution is to put assets on a CDN such as AWS S3. Since my needs were very simple I decided to include Bootstrap from a public CDN and write a few lines of inline CSS. Not the cleanest solution but that just worked.</p><h2>Traffic</h2><p>I released <a href="https://returntrue.win/">returntrue.win</a> in February 2018 and it got a bit of attention on <a href="https://twitter.com/matthieunapoli/status/959918744213573635">Twitter</a> and <a href="https://www.reddit.com/r/PHP/comments/7x92e6/return_true_to_win/">Reddit</a>. The website received a decent amount of traffic, especially the first days where I watched with attention the lambdas scale up and down.</p><p>As you would expect, I did not have anything to do except let Amazon take care of running my code for me.</p><p>Over the first month, the website served <strong>400,000 HTTP requests</strong> which resulted in <strong>650,000 lambda executions</strong> (remember that a user running a piece of code means 2 lambda executions: the website plus the "eval-as-a-service").</p><p><img src="https://mnapoli.fr/images/posts/returntrue-stats.png" alt="" /></p><h2>Costs</h2><p>As a developer I enjoyed not worrying about whether my server would be able to handle the traffic. However I have to admit I was worried about how much this would cost me in the end :)</p><p>In total, the first month <strong>cost me $3</strong>.</p><p>Needless to say I was relieved! The website has been running since and I pay close to $0 every month.</p><p>The majority of the cost was related to API Gateway as AWS Lambda has a generous free tier. Let's break it down:</p><ul><li>AWS Lambda: $0 thanks to the free tier, would have paid $2.78 otherwise</li>
<li>API Gateway: $2.39:
<ul><li>$2.23 for the HTTP requests</li>
<li>$1.16 for the 1.8Gb of data transfer</li>
</ul></li>
<li>DynamoDB: $0 thanks to the free tier, would probably be around $0.6 without the free tier</li>
<li>AWS CloudWatch: $0</li>
<li>Data Transfer: $0.04 - I still don't understand completely what this is about</li>
<li>Taxes: $0.5 :)</li>
</ul><h2>Conclusion</h2><p>While I hope this case study is useful, there are many things that could be improved:</p><ul><li>storing assets on a CDN</li>
<li>using a real PHP framework</li>
<li>caching HTTP requests (many many requests could be cached, resulting in an even lower cost)</li>
</ul><p>And the website could definitely have a better design. I hope to open source the website at some point to allow users to contribute new levels.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-case-study-returntrue/"/>
    <updated>2018-10-29T06:10:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-laravel/</id>
    <title><![CDATA[Serverless Laravel]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">25 May 2018 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><strong>Update: Since November 2018 AWS Lambda supports PHP via <em>custom runtimes</em>. Bref has changed to take advantage of that and the Laravel integration has changed accordingly. Read more about it <a href="https://bref.sh/docs/frameworks/laravel.html">in Bref's documentation for Laravel</a>.</strong></p><p>The article below is now obsolete.</p><hr /><p>Last week I introduced <a href="https://mnapoli.fr/serverless-php/">Bref as a solution to running PHP serverless</a>.</p><p>Today let's try to deploy a Laravel application on AWS lambda using <a href="https://github.com/mnapoli/bref">Bref</a>. The code shown in this article is <a href="https://github.com/mnapoli/bref-laravel-demo">available on GitHub</a>.</p><p><strong>You can check out the demo Laravel application on AWS lambda here: <a href="https://k6ay4xiyld.execute-api.eu-west-3.amazonaws.com/dev">https://k6ay4xiyld.execute-api.eu-west-3.amazonaws.com/dev</a>.</strong> It is a simple application that uses a 3rd party HTTP API to convert between currencies.</p><h2>An introduction to serverless PHP</h2><p>Serverless basically means "Running apps without worrying about servers". The main difference with a traditional hosting is that you do not maintain the servers and reserve their capacity. They are scaled up or down automatically and you pay only for what you use.</p><p>One notable example is AWS S3: instead of renting a fix disk space, you pay for only the storage you are actually using. AWS S3 has no limits, you can store 10kb or 10Tb and you do not need to scale up or down the storage. <strong>AWS S3 is serverless file storage.</strong></p><p>The idea behind serverless applications, aka Function as a Service (FaaS) is to apply the same principles:</p><ul><li>traditional hosting:
<ul><li>pay for fixed server resources (CPU, RAM…)</li>
<li>if you hit the limit you need to scale up the server</li>
<li>you pay for the whole server even if your application has no traffic</li>
</ul></li>
<li>serverless hosting:
<ul><li>no fixed server resources</li>
<li>the application is run whenever there is traffic</li>
<li>there are as many processes running as there are concurrent requests</li>
<li>you pay only for the execution time, an application with low traffic will have costs close to $0</li>
</ul></li>
</ul><p>Serverless hosting has the advantages of scaling very well since there are (theoretically) no limits. It can also help optimize costs by avoiding paying for unused server resources. You can read more about <a href="https://mnapoli.fr/serverless-php/#advantages">advantages and drawbacks here</a>.</p><h3>Making PHP work on AWS Lambda</h3><p>I will take the example of AWS Lambda because it is the most popular provider for serverless applications, but there are <a href="https://serverless.com/framework/docs/">other providers available</a>. Unfortunately AWS Lambda does not support PHP (supported languages are for example Javascript, Go, Python…). To run PHP we must add the PHP binary in the lambda and have, for example, Javascript execute it.</p><p>The technical details can be found <a href="https://mnapoli.fr/serverless-php/#making-php-work-on-aws-lambda">in this section</a> so I will not cover them again. The conclusion is that deploying PHP on serverless providers is possible but a PITA. That is how Bref is born.</p><p>Bref's goals are:</p><ul><li>deploy easily on serverless providers</li>
<li>make PHP frameworks work just like before</li>
</ul><p>For the first part Bref builds on top of <a href="https://serverless.com/">the serverless framework</a> and brings additional tooling specific to PHP.</p><p>For the second part I have been working on bridges to make PHP frameworks work on lambdas.</p><h2>Laravel on lambdas</h2><p><strong>What follows is a step-by-step explanation, if you want the short version either check out <a href="https://github.com/mnapoli/bref/blob/master/docs/Laravel.md">the Bref documentation</a> or <a href="https://github.com/mnapoli/bref-laravel-demo">the demo on GitHub</a>.</strong> I will also focus on AWS Lambda here because it is what I used for those tests.</p><p>Let's start by creating a new Laravel application:</p><pre class="language-shell">$ composer create-project laravel/laravel demo
$ cd demo</pre><p>Now let's install Bref (read the <a href="https://github.com/mnapoli/bref#setup">setup instructions</a>) and initialize it:</p><pre class="language-shell">$ composer require mnapoli/bref
$ vendor/bin/bref init</pre><p>When applications run on lambdas they do not work like with traditional hosting providers: <code>public/index.php</code> is no longer the application's entry point because there is no Apache, Nginx or PHP-FPM. Bref takes care of connecting the HTTP layer (API Gateway + AWS lambda integration) to your application. It will convert the HTTP request from API Gateway into a format Laravel can understand, and will convert back the HTTP response the other way around.</p><p><img src="https://mnapoli.fr/images/posts/serverless-laravel.png" alt="" /></p><p><em>Note: you don't need to know about API Gateway, Bref takes care of that part.</em></p><p>All we need to do is write a <code>bref.php</code> file: that will be the entrypoint of the application. Let's write that file at the root of the project:</p><pre class="language-php">&lt;?php
define('LARAVEL_START', microtime(true));
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
$kernel = $app-&gt;make(Illuminate\Contracts\Http\Kernel::class);
$app = new \Bref\Application;
$app-&gt;httpHandler(new Bref\Bridge\Laravel\LaravelAdapter($kernel));
$app-&gt;run();</pre><p>As you can see it's very similar to <code>public/index.php</code> except that Laravel will not run itself, instead Bref will run Laravel.</p><p>That is done using the <code>$app-&gt;httpHandler(...)</code> line (<a href="https://github.com/mnapoli/bref#http-applications">documentation</a>). You can use any PSR-17 framework as a HTTP handler, here we are configuring the Laravel HTTP Kernel using an adapter. The adapter is necessary because Laravel is not compliant with PSR-17 yet.</p><p>The next step is to configure which directories to deploy on the lambda. That can be configured in the <code>serverless.yml</code> file that was created in your project:</p><pre class="language-yaml">package:
  exclude:
    # ...
  include:
    # ...
    # Add the following directories:
    - 'app/**'
    - 'bootstrap/**'
    - 'config/**'
    - 'resources/**'
    - 'routes/**'
    - 'vendor/**'</pre><p>Now all we would have to do in theory would be to deploy using:</p><pre class="language-shell">$ vendor/bin/bref deploy</pre><p>But with Laravel we have a few extra steps to take.</p><h2>File storage</h2><p>The filesystem on lambdas is read-only, except for the <code>/tmp</code> folder. That means that Laravel's <code>storage</code> directory will not work out of the box if we keep it inside our project.</p><p>We need to tell Laravel to use the <code>/tmp/storage</code> folder instead. Add this line in <code>bootstrap/app.php</code> after <code>$app = new Illuminate\Foundation\Application</code>:</p><pre class="language-php">/*
 * Allow overriding the storage path in production using an environment variable.
 */
$app-&gt;useStoragePath(env('APP_STORAGE', $app-&gt;storagePath()));</pre><p>By using an environment variable we can keep the classic behavior when running the application locally (or on a traditional server) and we can set this variable to <code>/tmp/storage</code> on AWS lambda.</p><p>To set environment variables on AWS lambda we can use <code>serverless.yml</code>:</p><pre class="language-yaml">functions:
  main:
    ...
    environment:
      APP_STORAGE: '/tmp/storage'</pre><p>Now since this directory doesn't exist by default on AWS lambda we need to create it on the fly. Add these lines to <code>bref.php</code>:</p><pre class="language-php">// ...
$app = require_once __DIR__.'/bootstrap/app.php';
// Laravel does not create that directory automatically so we have to create it
if (!is_dir(storage_path('framework/views'))) {
    if (!mkdir(storage_path('framework/views'), 0755, true)) {
        die('Cannot create directory ' . storage_path('framework/views'));
    }
}
// ...</pre><h2>Configuration</h2><p>There are other parameters we will want to override for the production environment. What we will do is create a <code>.env.production</code> file in our project and configure our variables here:</p><pre class="language-dotenv">APP_ENV=production
APP_DEBUG=false
# ...</pre><p>To make Laravel use this configuration in production we will write a <em>build hook</em>. Build hooks are scripts that are executed <strong>before</strong> Bref deploys your application. Let's write those in a <code>.bref.yml</code> file:</p><pre class="language-yaml">hooks:
    build:
        # Rename the `.env.production` file to `.env`
        - 'rm .env &amp;&amp; cp .env.production .env'</pre><p><em>Note: those commands are run in a separate directory than your project, your own <code>.env</code> file will <strong>not</strong> be deleted.</em></p><p>By default Bref installs Composer dependencies and optimizes the autoloader, we do not need to do it ourselves.</p><p>In order to optimize the application we want to <a href="https://laravel.com/docs/5.6/configuration#configuration-caching">cache the configuration</a>. Let's add other hooks to the list:</p><pre class="language-yaml">hooks:
    build:
        # Use the `.env.production` file as `.env`
        - 'rm .env &amp;&amp; cp .env.production .env'
        - 'rm bootstrap/cache/*.php'
        - 'php artisan config:cache'</pre><p>The <code>php artisan config:cache</code> dumps the optimized configuration files. The problem with that is that it will also dump all file paths as absolute paths (e.g. storage directory, views, etc.). Since Laravel is in a different directory on our computer than on AWS lambda the dumped configuration will not work in production.</p><p>We need to tell Laravel to generate <strong>relative paths</strong>. Let's do that by customizing the root path of Laravel in <code>bootstrap/app.php</code> as shown below. The <code>APP_DIR</code> variable will allow us to replace the absolute path by <code>.</code> (relative path) when generating the lambda.</p><pre class="language-php">$app = new Illuminate\Foundation\Application(
    env('APP_DIR', realpath(__DIR__.'/../'))
);</pre><p><em>Note: we do not want to hardcode <code>.</code> directly here because that would mess up Laravel's behavior in other cases (for example with <code>artisan serve</code> or when running Laravel with Apache/Nginx).</em></p><p>Let's add that new environment variable to <code>serverless.yml</code>:</p><pre class="language-yaml">functions:
  main:
    ...
    # Laravel configuration using environment variables:
    environment:
      APP_STORAGE: '/tmp/storage'
      APP_DIR: '.'</pre><p>Let's also add that variable into our <code>.env.production</code> file:</p><pre class="language-dotenv"># ...
# This allows to generate relative file paths for the lambda
APP_DIR=.
APP_STORAGE=/tmp/storage</pre><p>Everything is good except one last problem: view cache files. Laravel runs <code>realpath()</code> on the path containing the cached views, and we are generating a configuration with <code>/tmp/storage</code> on our machine -&gt; that directory doesn't exist on our machine, and <code>realpath()</code> fails. That breaks Laravel's views.</p><p>We need to remove the <code>realpath()</code> call in <code>config/views.php</code>:</p><pre class="language-diff">-    'compiled' =&gt; realpath(storage_path('framework/views')),
+    'compiled' =&gt; storage_path('framework/views'),</pre><p>All is good now!</p><h2>Logging</h2><p>We configured Laravel to use the <code>/tmp/storage</code> directory, but <code>/tmp</code> is ephemeral: everything in there will be lost at some point. This is not a good place to store logs.</p><p>We need to change the logging driver and instead use the <code>stderr</code> driver: logs will be captured by Bref and sent directly to <a href="https://aws.amazon.com/cloudwatch/">AWS Cloudwatch</a>. That can be configured in <code>.env.production</code>:</p><pre class="language-dotenv"># ...
LOG_CHANNEL=stderr</pre><p>You can of course use <a href="https://laravel.com/docs/5.6/logging">any other driver you want</a> (as long as it's not the <code>file</code> driver).</p><h2>Sessions</h2><p>Just like logging, storing sessions in <code>/tmp/storage</code> is not a good idea. If you are writing an API or any application that do not need sessions you can use the <code>array</code> driver:</p><pre class="language-dotenv"># ...
SESSION_DRIVER=array</pre><p>If you need sessions you can store them <a href="https://laravel.com/docs/5.6/session">in database, Redis, etc</a>.</p><h2>Routing</h2><p>Finally when deploying a lambda AWS creates a random URL that contains <a href="https://github.com/mnapoli/bref#why-is-there-a-dev-prefix-in-the-urls-on-aws-lambda">a <code>/dev</code> suffix</a>. Because of that the default route (<code>/</code>) will not work out of the box. We can change the route for the welcome page:</p><pre class="language-php">// routes/web.php
Route::get('/dev', function () {
    return view('welcome');
});</pre><p>If you use a custom domain however the suffix will disappear so it is only an issue when doing small tests like this.</p><h2>Deploying</h2><p>We are finally done for a functional serverless Laravel. Let's deploy:</p><pre class="language-shell">$ vendor/bin/bref deploy</pre><p>Our app should now be online, get the URL of the app by running <code>vendor/bin/bref info</code>.</p><h2>Going further</h2><p>There are still many things to improve with Bref and Laravel:</p><ul><li>simplify the setup and configuration</li>
<li>build the views before deploying (this is very hard because Laravel uses absolute paths again here, I hope to submit a solution for that soon)</li>
<li>integrate <code>artisan</code> to be able to run on lambdas</li>
<li>integrate queues and scheduling to run them on lambdas</li>
<li>integrate with CDN for hosting assets</li>
<li>etc.</li>
</ul><p>I am planning to work on those topics in the months to come, and help is always welcome.</p><h2>Conclusion</h2><p>If you want to learn more about Bref and Laravel:</p><ul><li>read the <a href="https://github.com/mnapoli/bref">Bref documentation</a></li>
<li>here is <a href="https://k6ay4xiyld.execute-api.eu-west-3.amazonaws.com/dev">the serverless Laravel demo</a></li>
<li>check out <a href="https://github.com/mnapoli/bref-laravel-demo">the code for the Laravel demo</a></li>
</ul><p>To learn more about serverless in general:</p><ul><li>check out this <a href="https://mnapoli.fr/serverless-php/">article about running PHP serverless</a></li>
<li>and this <a href="https://mnapoli.fr/serverless-php-performances/">article about performances</a></li>
</ul><p>If you want to get started I can also accompany you, drop me a line at matthieu at mnapoli dot fr.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-laravel/"/>
    <updated>2018-05-25T12:05:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-php-performances/</id>
    <title><![CDATA[Serverless and PHP: Performances]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">24 May 2018 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Last week I introduced <a href="https://mnapoli.fr/serverless-php/">Bref as a solution to running PHP serverless</a>.</p><hr /><p><strong>Update: Since November 2018 AWS Lambda supports PHP via <em>custom runtimes</em>. Bref now takes advantage of that, <a href="https://bref.sh/">you can read more about it at bref.sh</a>.</strong></p><p><strong>Performances have changed a lot with these new versions.</strong> This article is now obsolete.</p><hr /><p>Today let's explore what performances to expect when running PHP on AWS lambda using <a href="https://github.com/mnapoli/bref">Bref</a>. Everything shown in this article is <a href="https://github.com/mnapoli/bref-benchmark">open source on GitHub</a> and can be reproduced.</p><p>Everything shown here is specific to AWS Lambda. Other serverless providers (Azure, Google Cloud…) could offer different performances and costs.</p><h2>Results</h2><p>This is a comparison of the <strong>HTTP response time</strong> that I got by calling the lambdas from an EC2 machine in the same region, on both a NodeJS lambda and a PHP lambda. This is not the lambda's execution time but the real HTTP response time, network included. If you want to use lambdas in a non-HTTP context those results will still be useful.</p><p>I run the test more than 3 times for each combination and I keep the lowest one (because of cold starts numbers vary a lot at first).</p><p>I ran the test for several memory sizes: lambdas "sizes" are configured through the memory. More memory means more CPU, but also higher costs.</p><blockquote>
<p>AWS Lambda allocates CPU power proportional to the memory by using the same ratio as a general purpose Amazon EC2 instance type, such as an M3 type. For example, if you allocate 256 MB memory, your Lambda function will receive twice the CPU share than if you allocated only 128 MB.</p>
</blockquote><table><thead><tr><th>Memory</th>
<th>Node lambda</th>
<th>PHP lambda</th>
</tr></thead><tbody><tr><td>128M</td>
<td>28ms</td>
<td>340ms</td>
</tr><tr><td>512M</td>
<td>28ms</td>
<td>86ms</td>
</tr><tr><td>768M</td>
<td>21ms</td>
<td>59ms</td>
</tr><tr><td>1024M</td>
<td>20ms</td>
<td>46ms</td>
</tr><tr><td>2048M</td>
<td>21ms</td>
<td>42ms</td>
</tr><tr><td>3008M</td>
<td>21ms</td>
<td>46ms</td>
</tr></tbody></table><p><img src="https://mnapoli.fr/images/posts/serverless-performances.png" alt="" /></p><p>We can see that Node performances are pretty consistent, whereas PHP performances become optimal above 1024M. Performances with 512M can be acceptable depending on the use case (e.g. workers, crons, etc.). 128M performances are pretty poor.</p><p><strong>To sum up, we should expect a 20ms penalty to using PHP over AWS Lambda (using <a href="https://github.com/mnapoli/bref">Bref</a>) compared to other languages.</strong></p><p>What's interesting to note is that Node's 21ms base response time is because of the HTTP layer (API Gateway and network). Let's consider this other graph (Cloudwatch metrics):</p><p><img src="https://mnapoli.fr/images/posts/serverless-cloudwatch.png" alt="" /></p><ul><li>blue line: HTTP response time (~40ms for PHP, 13ms for Node)</li>
<li>green line: PHP execution time (25ms)</li>
<li>orange line: Node execution time (0ms)</li>
</ul><p>This confirms that PHP adds 20ms-25ms to the lambda's execution time. The HTTP layer (API Gateway) adds ~15ms in all cases. The network between the lambdas and the EC2 machine used for the tests accounts for ~5ms.</p><h2>Cold starts</h2><p>The numbers above are the "cruise" numbers, when everything is warmed up and runs smoothly. But lambda cold starts should not be ignored. I will not cover that topic again since it has been covered heavily elsewhere, I can recommend reading <a href="https://hackernoon.com/im-afraid-you-re-thinking-about-aws-lambda-cold-starts-all-wrong-7d907f278a4f">this article</a>.</p><p>Here are measurements made on Node and PHP lambdas, completed with <a href="https://read.acloud.guru/does-coding-language-memory-or-package-size-affect-cold-starts-of-aws-lambda-a15e26d12c76">cold starts from other languages found in this comprehensive article</a>.</p><table><thead><tr><th>Memory</th>
<th>Python</th>
<th>Node</th>
<th>PHP</th>
<th>Java</th>
<th>C#</th>
</tr></thead><tbody><tr><td>128M</td>
<td>1ms</td>
<td>21ms</td>
<td>1261ms</td>
<td>3562ms</td>
<td>4387ms</td>
</tr><tr><td>512M</td>
<td>0ms</td>
<td>3ms</td>
<td>336ms</td>
<td>999ms</td>
<td>1223ms</td>
</tr><tr><td>768M</td>
<td>2ms</td>
<td>231ms</td>
</tr><tr><td>1024M</td>
<td>0ms</td>
<td>2ms</td>
<td>210ms</td>
<td>530ms</td>
<td>524ms</td>
</tr></tbody></table><p><img src="https://mnapoli.fr/images/posts/serverless-coldstarts.png" alt="" /></p><p><strong>PHP's cold start is lower than Java and C#</strong> even though both those languages are supported natively by AWS Lambda. From 768M and up the cold starts stabilize around 230ms.</p><p>We can also see that Python has lower cold starts than Node. Bref could switch to Python as the language used to invoke PHP but the gain seems to be minimal compared to PHP's execution time.</p><h2>Costs</h2><p>Let's overlay the PHP performances and the cost associated. For simplicity of reading, and because the base response time is 340ms, I will be counting 1 million lambda requests that take 400ms to execute.</p><p><strong>Note that this do not include the cost of API Gateway, data transfers, etc.</strong> (which can be much higher than that). I also do not include the free tier because the goal is to compare each row with the others.</p><p>I have removed 20ms of the execution time to go from the HTTP response time to the actual lambda's execution time.</p><table><thead><tr><th>Memory</th>
<th>Base execution time</th>
<th>Total cost for 1M executions of a 400ms lambda</th>
<th>Cost of the base execution time</th>
</tr></thead><tbody><tr><td>128M</td>
<td>320ms</td>
<td>$1</td>
<td>$0.67 for 320ms</td>
</tr><tr><td>512M</td>
<td>66ms</td>
<td>$3.5</td>
<td>$0.55 for 66ms</td>
</tr><tr><td>768M</td>
<td>39ms</td>
<td>$5.2</td>
<td>$0.49 for 39ms</td>
</tr><tr><td>1024M</td>
<td>26ms</td>
<td>$6.87</td>
<td>$0.43 for 26ms</td>
</tr><tr><td>2048M</td>
<td>22ms</td>
<td>$13.5</td>
<td>$0.74 for 22ms</td>
</tr><tr><td>3008M</td>
<td>26ms</td>
<td>$19.7</td>
<td>$1.29 for 26ms</td>
</tr></tbody></table><p>Using the 1024M lambdas to get great performances with PHP mean a 6 times increase on costs. <strong>However this does not take into account that lambdas will also run faster, bringing costs down</strong>, which is what we can see in the last column (cost associated to the base execution time is actually decreasing). On the other hand most applications will not run 6 times faster because often the CPU is not the most limiting factor (this is rather I/O like network, databases…).</p><p>Like in all situation it is best to make your own tests to get a better idea. If your lambda is invoked through HTTP the major cost will be API Gateway anyway.</p><h2>Conclusion</h2><ul><li>use 512M memory for acceptable performances, 1024 and up for optimal performances</li>
<li>expect a 20ms penalty to using PHP over AWS Lambda compared to other languages</li>
<li>PHP's cold start is lower than other languages like Java and C#, from 768M and up the cold start stabilizes around 230ms</li>
</ul><p>Want to get started: have a look at <a href="https://github.com/mnapoli/bref">Bref</a>.</p><p>Update: I realize I did not mention opcache which is an important player in PHP performances. I did not forget about it, out of the box it is not supported because PHP is running as CLI. However I am planning to work on that, opcache can be used in CLI processes.</p>]]></summary>
    <link href="https://mnapoli.fr/serverless-php-performances/"/>
    <updated>2018-05-24T12:05:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/serverless-php/</id>
    <title><![CDATA[Serverless and PHP: introducing Bref]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">17 May 2018 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Serverless basically means "Running apps without worrying about servers". Obviously <a href="http://www.commitstrip.com/en/2017/04/26/servers-there-are-no-servers-here/">there are still servers involved</a>, the main difference is that you do not maintain the servers and reserve their capacity. They are scaled up or down automatically and you pay only for what you use.</p><p>This article intends to explain what serverless means for web applications and more specifically for PHP.</p><hr /><p><strong>Update: Since November 2018 AWS Lambda supports PHP via <em>custom runtimes</em>. Bref now takes advantage of that, <a href="https://bref.sh/">you can read more about it at bref.sh</a>.</strong></p><hr /><h2>What is serverless?</h2><h3>Serverless file storage</h3><p>AWS S3 and other file/object storage are good examples of serverless infrastructure.</p><p>If you were to store files in your application (for example file uploads, images, etc.), the traditional solution would be to store them on a drive mounted in the server. The downsides would be that you would have to monitor it and scale up the volume in case it gets full. You would also pay for the whole storage space even if you used 10% of it.</p><p>The serverless approach is rather a virtual storage with no maximum capacity and managed for you. You would also pay only for the disk space you are actually using. That is what AWS S3 offers for example.</p><h3>Serverless databases</h3><p>The traditional approach to using a database in an application is to run MySQL/PostgreSQL/etc. on a server. You would have to maintain it (monitoring, backups, replication…) and the database would be limited to the resources available on the server (CPU, RAM, storage…). If you hit those limits you would have to scale the server up, which means effort and downtime. In the opposite case if the application barely uses the database you would still pay for the whole server.</p><p>The serverless approach is again to abstract the server away and hide how the database service is running. You would use the database just as before, however the resources dedicated to your database would scale up or down dynamically with the load. Monitoring, backups and replication would be handled for you. You would pay only for the resources (CPU/RAM/storage) the database actually uses.</p><p>While using serverless file storage is becoming more common, serverless databases are still a bit new. This is probably because databases are a lot trickier to manage than files. Good examples of serverless databases would be AWS DynamoDB (which is a NoSQL database) and "AWS Aurora serverless" which replicates MySQL or PostgreSQL. Note however that for now AWS Aurora serverless is still in private beta.</p><p>The same could be said about other kind of databases like message queues, caches, etc.</p><h3>Serverless applications, aka FaaS</h3><p>The "serverless" approach for applications is a bit trickier to imagine.</p><p>The "traditional" approach to running an application means executing the application on a server. That application would be a process running on a CPU and some RAM. The application would either <em>act</em> immediately (like an application executed on the command line, a CRON task…) or <em>listen and react</em> to external events (an incoming HTTP request, a TCP connection, a new message in a message queue…), aka a <em>daemon</em>.</p><blockquote>
<p>For example the code for a HTTP API in NodeJS would contain:</p>
<ul><li>
<p>the main script, starting a HTTP server on port 80, and a router handling requests, often through a framework like Express</p>
<ul><li>
<p>a controller (JS function) that is called when a HTTP request calls GET <code>/users</code></p>
</li>
<li>
<p>a controller (JS function) that is called when a HTTP request calls POST <code>/users</code></p>
</li>
</ul></li>
</ul></blockquote><p>The serverless approach takes things differently: instead of thinking about applications and processes, the application is considered as a collection of <strong>functions</strong>, also known as <strong>lambdas</strong>.</p><p>FaaS, which means <em>Function as a Service</em>, means that you only care about writing the function and that the FaaS provider will take care of running them.</p><p>Lambdas are invoked by events that can be:</p><ul><li>a HTTP request</li>
<li>a new message in a message queue</li>
<li>a new file in a file storage (like an AWS S3 bucket)</li>
<li>a specific time to replicate the behavior of a cron (e.g. every day at 2am)</li>
<li>etc.</li>
</ul><blockquote>
<p>If we take the same example as before, the serverless equivalent would be a NodeJS project containing 2 functions:</p>
<ul><li>a function that is set up to be called when a HTTP request calls GET <code>/users</code></li>
<li>a function that is set up to be called when a HTTP request calls POST <code>/users</code></li>
</ul></blockquote><p>The serverless tooling takes care of the <em>daemon</em> part (e.g. the HTTP server and routing) and developers now have just to write multiple functions.</p><p>On AWS the HTTP server + routing is taken care of by API Gateway (you would basically define routes there), and the execution of the functions is done by AWS Lambda (a bit like Docker containers).</p><blockquote>
<p>On a side note you do not necessarily have to split your application into many lambdas (one for each controller). You could also have 1 lambda that do the routing like before.</p>
<p>I do not know yet if one way is fundamentally better than the other, but this is definitely easier to get started with 1 lambda. This approach is also closer to current architectures and frameworks, especially in PHP where we migrated from multiple <code>*.php</code> files into a single <code>index.php</code> entrypoint a long time ago. I am not convinced that splitting everything again makes sense. I guess time will tell.</p>
</blockquote><h4>Advantages</h4><p>Just like file storage and database, the main advantages are costs, scalability and the reduced amount of server maintenance.</p><p>Lambdas are executed on demand whenever they are triggered by an event (like an HTTP request). That means that if your application has no activity, nothing is running and the costs are close to 0. When your application receives a lot of requests, the FaaS provider runs as many instances as necessary to handle them. If there were to be 1000 HTTP requests at the same time, your lambda would execute 1000 times in parallel. This is useful for HTTP applications but also workers: you don't have to run a fixed number of workers at all time. Of course you could be limited by other non-scalable components of your architecture, like the database, if those are not "serverless" and scalable on-demand too.</p><p>You usually pay by the time of execution of your lambdas. Try the <a href="https://s3.amazonaws.com/lambda-tools/pricing-calculator.html">AWS Lambda pricing calculator</a> to get an idea. For HTTP applications however you will also pay for API Gateway and data transfer, and those can cost more than AWS Lambda.</p><h4>Drawbacks</h4><p>First the technology is new, compared to traditional solutions the tooling is basic and not mature. There is less expertise and less resources out there to get started easily and build something correctly. And since we lack perspective on the subject it is harder to distinguish the use cases where serverless makes perfect sense VS the cases were serverless is not useful.</p><p>Costs can be a drawback too: if you compare the cost of a high traffic application hosted on lambdas vs a lower level solution like bare-metal or VPS hosting, it's possible that serverless will be more expensive. This is because you pay for the CPU/RAM resources <em>and the service</em> of managing the infrastructure. If you have an infrastructure that is already working fine as it is, the switch to serverless may not be worth it.</p><p>A drawback usually brought up is vendor lock-in. While it is a legitimate concern, it is more and more mitigated as there are open solutions that provide alternatives to AWS and Azure (e.g. serverless on Kubernetes on your own machines). There are more and more tools abstracting the provider, allowing (theoretically) to switch from one provider to another more easily. I think that given enough time there will be as much vendor lock-in as there is with any hosting provider out there, i.e. not zero but acceptable (after all, switching hosting provider is always a cost, even with tools like Ansible, Docker, etc.).</p><p>Finally, a big drawback is that the serverless approach also impacts our code and our frameworks. Those are often not ready to work out of the box with lambdas because they were not imagined for those environments and architectures. While there is often not a huge effort required, it still is an effort (PHP is a good example for that and that is detailed below).</p><h2>Serverless and PHP</h2><hr /><p><strong>Update: Since November 2018 AWS Lambda supports PHP via <em>custom runtimes</em>. The new version of Bref takes advantage of that, <a href="https://bref.sh/">you can read more about it at bref.sh</a>.</strong></p><p>What is described below is now obsolete.</p><hr /><p>PHP applications are different than NodeJS/Java/Go web applications because they rely on Apache/Nginx + PHP-FPM for the HTTP server layer. In the NodeJS example, the application would start and listen on a port for HTTP requests, the same process would handle all the HTTP requests.</p><p>With PHP, each HTTP request gets its own process. When the HTTP response is emitted, the PHP application terminates and everything is cleared so that on each new HTTP request the PHP application starts from scratch again (this is a simplification). This behavior is in the end very similar to lambdas: for each HTTP request a lambda is booted, handles the request and dies (this is also a simplification). That is why I think lambdas make sense for PHP: the architectural gap is not huge.</p><h3>Making PHP work on AWS Lambda</h3><p>I will take the example of AWS Lambda because it is the most popular provider for serverless applications. Unfortunately AWS Lambda does not support PHP (supported languages are for example Javascript, Go, Python…). To run PHP we must add the PHP binary in the lambda and have, for example, Javascript execute it.</p><p>To sum up, what we will have to do:</p><ul><li>compile PHP for the OS used on lambdas</li>
<li>add the compiled PHP binary to the lambda</li>
<li>write a Javascript handler (the code executed by the lambda) that executes the PHP binary</li>
<li>write a PHP handler (the code that will be executed by the Javascript handler)</li>
<li>deploy the lambda</li>
</ul><p>The content of the lambda would look like this:</p><pre class="language-shell">bin/
    php      # our compiled PHP binary
handler.js   # executes `bin/php handler.php`
handler.php  # the PHP code we want to run on the lambda</pre><p>Now that we have something working, let's add the missing parts: the input and output. Lambdas take <em>event data</em> as input, and return a <em>response</em>. The event data can contain data passed by the caller, or if we are in the context of a HTTP request the event will contain the HTTP headers, request, URI, etc. The response can contain anything we want to return to the caller, and for a HTTP context it should contain the HTTP response.</p><p>Since <code>handler.js</code> receives the event data directly, we can pass it to <code>handler.php</code> through several ways: using a specific temporary file for example, or more simply through a command parameter. That means running <code>bin/php handler.php &lt;the-event-data&gt;</code>. The event data would be encoded as JSON, read by the PHP script using the <a href="http://php.net/manual/en/reserved.variables.argv.php"><code>$argv</code> variable</a> and decoded using <code>json_decode()</code>.</p><p>For the response, <code>handler.php</code> could write the response in a temporary file or event output it on <code>stdout</code>, then <code>handler.js</code> would read it and return it as the response of the lambda.</p><p>Here is <a href="https://github.com/mnapoli/bref/blob/0.2.5/template/handler.js">an example of a <code>handler.js</code></a> if you are curious.</p><h2>Introducing Bref</h2><p>Needless to say that the above solution is not ideal because:</p><ul><li>you have to compile PHP manually</li>
<li>you have to write and maintain a <code>handler.js</code> for your PHP app</li>
<li>you have to know the format of AWS lambda's event and response (for example for HTTP requests and responses)</li>
<li>you cannot connect lambda's events and responses to PHP frameworks input/output or HTTP requests/responses</li>
<li>you need to setup a deployment process from scratch to compile PHP, setup the project for the readonly filesystem (e.g. Composer install, prepare the caches…)</li>
</ul><p>At first I tried to write a plugin for <a href="https://serverless.com/">the serverless framework</a> to support PHP but that didn't work out because of <a href="https://github.com/serverless/serverless/issues/4563">some limitations</a>, and because that project is mostly about deployments.</p><p>I believe there are 2 problems to solve for the "serverless PHP" equation:</p><ul><li><strong>deploy easily on serverless providers</strong></li>
<li><strong>make PHP frameworks work just like before</strong></li>
</ul><p><em>I decided to write <a href="https://github.com/mnapoli/bref">Bref</a> to solve those problems.</em></p><blockquote>
<p>The name <code>bref</code> means <code>brief</code> in french, in reference to the ephemeral life of lambdas. French speakers will also enjoy the double meaning of "Bref, j'ai déployé une application serverless" ;)</p>
</blockquote><p>The deployment is implemented by a CLI tool, <code>bref deploy</code>, that wraps the serverless framework to add what is needed for PHP. On deployment, Bref will automatically install Composer dependencies (excluding dev dependencies) and optimize the autoloader for production. It is also possible to add additional tasks like building a cache before deployment (which is required by some frameworks like Symfony).</p><p>A simple PHP lambda can be implemented like this:</p><pre class="language-php">&lt;?php
require __DIR__.'/vendor/autoload.php';
$app = new \Bref\Application;
$app-&gt;simpleHandler(function (array $event) {
    return [
        'hello' =&gt; $event['name'] ?? 'world',
    ];
});
$app-&gt;run();</pre><p>While that works, that does not solve the second problem because that does not work with PHP frameworks. I decided to tackle 2 ways of integrating with PHP frameworks:</p><ul><li>HTTP applications</li>
<li>CLI applications</li>
</ul><h4>HTTP applications</h4><p>PHP frameworks usually read the request from global variables, and echo the HTTP response to PHP's output and headers to global functions. That does not work on lambdas.</p><p><strong>Fortunately modern frameworks abstract the HTTP request and responses with objects.</strong> What's even better is that we have PSRs for that (PSR-7 and PSR-15).</p><p>What Bref does is turn the lambda's event data (that contains HTTP request data) into a PSR-7 request object. We now have a HTTP request that frameworks can process. We call the framework with that request and get the PSR-7 response in return. We turn that response object into a valid AWS lambda response and return that to <code>handler.js</code>.</p><p>By abstracting that with PSR-15's <a href="https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php#L12"><code>RequestHandlerInterface</code></a> (and adapters for frameworks that are not directly compatible) we can support most frameworks with very little effort for the end user.</p><p>Here is an example with the <a href="https://www.slimframework.com/">Slim micro-framework</a>:</p><pre class="language-php">$slim = new Slim\App;
$slim-&gt;get('/dev', function ($request, $response) {
    $response-&gt;getBody()-&gt;write('Hello world!');
    return $response;
});
// Instead of calling `$slim-&gt;run()` we use Bref
$bref = new Bref\Application;
$bref-&gt;httpHandler(new Bref\Bridge\Slim\SlimAdapter($slim));
$bref-&gt;run();</pre><p>In short we use PHP frameworks just like usual but instead of running the framework directly we pass it to <code>$bref-&gt;httpHandler(...)</code> (for those curious <a href="https://github.com/mnapoli/bref/blob/8946084aa194012418d1af468d75e3cf436ac579/src/Application.php#L120-L123">here is what happens with the handler</a> when <code>$bref-&gt;run()</code> is called).</p><h4>CLI applications</h4><p>Many frameworks let us create CLI applications (Symfony Console, Laravel Artisan, <a href="https://github.com/mnapoli/silly">Silly</a>…). Just like with HTTP, those frameworks read from global variables and output to <code>stdout</code> which doesn't work on lambdas.</p><p>But again like with HTTP, frameworks have built object-oriented abstraction over the CLI input and output.</p><p>Bref adds support to executing CLI commands in production using the <code>bref cli</code> command:</p><ul><li>run <code>bref cli -- [arguments and options]</code> on your machine (everything after the <code>--</code> is considered as arguments and options to the target CLI command)</li>
<li>the <code>bref</code> CLI tool will invoke the lambda and encode the provided arguments and options in the event data</li>
<li>the lambda will recognize that it is called as a CLI and will decode the arguments/options from the event array into <code>Input</code> and <code>Output</code> objects</li>
<li>Bref will run the CLI framework (Symfony Console, etc.) with those objects</li>
<li>it will encode the output into the lambda's response</li>
<li>the <code>bref</code> CLI tool receives the response and displays the output</li>
</ul><p>Here is an example with a <a href="https://github.com/mnapoli/silly">Silly</a> application (which is a wrapper around the Symfony Console):</p><pre class="language-php">$silly = new \Silly\Application;
$silly-&gt;command('hello [name]', function (string $name = 'World!', $output) {
    $output-&gt;writeln('Hello ' . $name);
});
$app = new \Bref\Application;
$app-&gt;cliHandler($silly);
$app-&gt;run();</pre><p>Running the command looks like this:</p><pre class="language-bash">$ bref cli -- hello Bob
Hello Bob
# running the same command locally:
$ bin/console hello Bob
Hello Bob</pre><h3>State of the project</h3><p>I want to emphasize that Bref is currently a beta project. As explained above serverless architectures is still new and many choices in Bref are not definitive. My goal in sharing this project is helping others try out serverless, and to help moving forward.</p><p>Just like with any buzzword there is hype, and there is distrust because of the hype. Bref is meant to help you make your own mind, not sell you a silver bullet.</p><p>The project is new, there are many limitations and drawbacks and there is still a lot to improve. I take the habit of <a href="https://github.com/mnapoli/bref/search?utf8=%E2%9C%93&amp;q=todo&amp;type=">filling the code with very detailed TODOs</a>, contributors are welcome. Regarding performances (because I know that's in everyone's mind) there is obviously a penalty to executing PHP CLI like this on every request. <a href="https://read.acloud.guru/serverless-php-630bb3e950f5">Some benchmarks</a> estimate it at 20ms per request, which is acceptable in many scenarios. I also think there is a lot of room for improvements, for example <a href="https://twitter.com/matthieunapoli/status/987798656349868033">regarding opcache</a>. I don't want to get too hung up on that.</p><p>I am using the project in production on a few small scale projects and it does the job. Like semver allows, I am keeping BC only between patch versions while in a <code>0.*.*</code> numbering. In Composer you should use <code>~0.x.0</code> instead of <code>^0.x.0</code> to avoid BC breaks until a <code>1.0.0</code> version.</p><h3>Case studies</h3><p>As explained above most of this is quite new, I am collecting case studies to show what we can expect from serverless PHP applications.</p><ul><li>
<p><a href="https://returntrue.win/">returntrue.win</a> is a serverless application (<a href="https://twitter.com/matthieunapoli/status/995952637450350592">original tweet</a>)</p>
<p>The web application is one lambda. The code evaluation is done in another lambda. The database is very small and stored in DynamoDB.</p>
<p>When the project was released it got 350k HTTP requests in 2 weeks, and 700k invocations across the 2 lambdas over 1 month. Not matter the trafic the performances were always the same. The total cost for that period was $3.</p>
</li>
<li>
<p><a href="https://github.com/mnapoli/bref-symfony-demo">bref-symfony-demo</a> is a Symfony application deployed as a lambda (<a href="https://twitter.com/matthieunapoli/status/996296761101770753">original tweet</a>)</p>
</li>
<li>
<p>serverless workers: I have been running workers in AWS lambda in a private project for several months now and I am very happy with the processing time (no queuing time since lambdas are run on demand), the costs ($0) and the maintenance (very low)</p>
</li>
</ul><p>Please share your own experience in the comments.</p><h2>TL/DR</h2><p>Want to try out PHP as a serverless applications? Have a look at <a href="https://github.com/mnapoli/bref">Bref</a>.</p><p>To clear up vocabulary confusions:</p><ul><li>Serverless: a type of hosting where servers are managed for you and allocated dynamically</li>
<li>FaaS (Function as a Service): serverless for code</li>
<li>Lambda: a function</li>
</ul>]]></summary>
    <link href="https://mnapoli.fr/serverless-php/"/>
    <updated>2018-05-17T12:05:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/organizing-code-into-domain-modules/</id>
    <title><![CDATA[Organizing code into domain modules]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">26 November 2017 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><img src="https://mnapoli.fr/images/posts/code-modules.jpg" alt="" /></p><p><em>Note: the french version of this article is published on <a href="http://tech.wizaplace.com/posts/organisation-du-code-par-modules-fonctionnels">Wizaplace's tech blog</a></em>.</p><p>We recently discussed 2 topics seemingly unrelated with <a href="http://tech.wizaplace.com">my colleagues at Wizaplace</a>:</p><ul><li>how to organize code?</li>
<li>how to organize teams?</li>
</ul><h2>Feature teams</h2><p>Regarding "how to organize teams", we were discussing Spotify's <a href="https://labs.spotify.com/2014/03/27/spotify-engineering-culture-part-1/"><em>Feature teams</em></a>. In a "classic" organization, teams are usually formed by grouping people based on their job title:</p><ul><li>backend developers team</li>
<li>frontend developers team</li>
<li>sysadmin team</li>
<li>product team</li>
</ul><p>But in a "feature team" organization, teams are organized… by features:</p><ul><li>team <em>e-commerce</em>: 1 product owner, 3 developers, 1 sysadmin</li>
<li>team <em>blog</em>: 1 product owner, 2 developers, 1 sysadmin</li>
</ul><p>The pros of this kind of organization are numerous and I do not intend to go over them here. <strong>But what does this have to do with code organization?</strong></p><h2>The classic code structure</h2><p>Most projects will follow this "classical" layout:</p><pre>src/
    Entity/
        Product.php
        Basket.php
    Service/
        ProductService.php
        BasketService.php
    Repository/
        ProductRepository.php
        BasketRepository.php
    Resources/
        ORM/
            Product.orm.yml
            Basket.orm.yml</pre><p>Code is organized by type, i.e. what each thing <strong>is</strong> (an entity, a service, a repository, …).</p><p>A developer <em>is</em> a developer, a sysadmin is a sysadmin, but when they both work on the same project, it makes sense to let them be in the same team. How about doing the same thing with code? Why not group code based on what it <em>does</em> instead of what it <em>is</em>?</p><h2>Module-oriented code structure</h2><p>Here is an alternative layout where code is grouped by "domain module", or feature:</p><pre>src/
    BasketModule/
        Basket.php
        BasketService.php
        BasketRepository.php
        ORM/
            Basket.orm.yml
    ProductModule/
        Product.php
        ProductService.php
        ProductRepository.php
        ORM/
            Product.orm.yml</pre><p>The first reaction one could have when seeing this is:</p><blockquote>
<p>How do I find all the repositories (or entities) at once? I will be jumping between folders!</p>
</blockquote><p>To which one can answer: "<em>why would we want to find all repositories or entities?</em>" Humans interact with code for 2 reasons:</p><ul><li>to read it and understand it</li>
<li>to change it</li>
</ul><p>In my experience it is much more common to read code to understand a feature (for example "how is stock handled in products", "when is a product published", …) rather than to understand how <em>all</em> repositories work or to re-read <em>all</em> the config files.</p><p>The same goes for writing or modifying code: we often work on fixing or improving a feature, rather that e.g. change all repositories at once. For example adding a new field on products is much easier to do when all the relevant files are in one place.</p><h3>Dependencies and cohesion</h3><p>When we think about it, what do <code>ProductRepository</code> and <code>BasketRepository</code> have in common? Both address different problems and should be completely decoupled, so why group them together?</p><p>On the contrary, <code>Product</code>, <code>ProductService</code> and <code>ProductRepository</code> are highly coupled (and for good reasons). These classes will interact together and change together over time. Let's recall the definition of cohesion:</p><blockquote>
<p>Cohesion: degree to which the elements of a module belong together.</p>
</blockquote><h3>"Agile design"</h3><p>Another great consequence of that layout is that it leaves us more freedom when designing each module.</p><p>With the "classic" code structure it becomes a no-brainer to create a repository, map the entity using Doctrine, etc. That means duplicating and perpetuating existing solutions. One could argue that it's a good thing: it brings consistency across the whole project. But it also kills creativity and does not encourage to ask the right questions, such as:</p><ul><li>Is Doctrine the best solution for this module? Do we even need an ORM?</li>
<li>This repository contains only 2 small SQL queries, would it make more sense to get rid of it and bundle those in the service?</li>
<li>Does creating an entity make sense in that particular case?</li>
</ul><p>Just like Agile encourages team to find solutions that work <em>for them</em> (i.e. there are no one size fits all solutions), this approach to code structure encourages to think and design each module based on its use case.</p><h3>The differences between a "Product" and a "Product" in e-commerce</h3><p>When I joined <a href="http://www.wizaplace.com/en/home-en/">Wizaplace</a> I was very surprised by something in the code: the products (the basis for an e-commerce website) were modeled twice in two different ways:</p><ul><li>
<p><strong>The product in the back-office</strong> can be modified by the seller, it can be in an incomplete state, without stock, etc.</p>
<p>This product is editable (with all the validation rules that go with this) and it is stored in database using many tables.</p>
</li>
<li>
<p><strong>The product visible in the shop</strong> can be added to a basket and bought.</p>
<p>This product is a projection of the back-office product, with all its data denormalized into a single table (no need for "joins" when retrieving it). It is read-only and only "completed" and validated products are visible in the shop. For those interested, this is a form of <a href="https://martinfowler.com/bliki/CQRS.html">CQRS</a>.</p>
</li>
</ul><p>I was not impressed so much by the technical side of things, but rather by the fact that <strong>this made a lot of sense business-wise</strong>.</p><p>This "product read model" was not only a matter of caching or optimizing performances: we just do not do the same thing with those two concepts. Having two separate models in the code allows to separate the logic related to <em>catalog management</em> to the one related to the online shop.</p><blockquote>
<p>What does it have to do with code organization?</p>
</blockquote><p>With a "classical" code structure we would end up with 2 <code>Product</code> classes in the same folder, which would obviously be a problem. We could get around this by being creative with names but this is still confusing. Good naming does matter, but at another level. Here is an example of what we could have had:</p><pre>src/
    Entity/
        Product.php
        ProductReadModel.php
    Service/
        ProductService.php
        ProductReadModelService.php</pre><p>When we decided to reorganize the code into functional modules we were forced to better understand the domain and to better integrate its vocabulary and concepts into the code:</p><pre>src/
    Catalog/
        Product.php
        CatalogService.php
    PIM/
        Product.php
        PIMService.php</pre><p>Thanks to numerous exchanges with the product and domain experts, we managed to put words onto all of this:</p><ul><li>The <strong>catalog</strong>, like paper catalogs or the catalog of amazon.com, only contains validated and published products. Those cannot be edited, but they can be bought.</li>
<li>The <strong>PIM</strong> (<a href="https://en.wikipedia.org/wiki/Product_information_management">Product Information Management</a>) is a kind of CRM for products: this is where categories, products, descriptions, prices and more can be managed. There are even <a href="https://www.akeneo.com/">software products dedicated to solving this problem</a>.</li>
</ul><p>The same concepts are being modeled into the code, but in different ways. For those familiar with Domain Driven Design you may be thinking of <a href="https://martinfowler.com/bliki/BoundedContext.html"><em>Bounded Contexts</em></a> and you would be very right, however I do not think DDD should be a requirement to benefit from that kind of code structure.</p><hr /><p>To conclude, organizing code into domain modules is not a silver bullet but it forces to better understand the problem we are solving and better structure our code.</p>]]></summary>
    <link href="https://mnapoli.fr/organizing-code-into-domain-modules/"/>
    <updated>2017-11-26T06:11:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/speaker-checklist/</id>
    <title><![CDATA[My "speaker checklist"]]></title>
    <summary><![CDATA[<article><header class="clearfix">
<p class="article-info text-muted">2 November 2017 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><img src="https://mnapoli.fr/images/posts/have-fun.jpg" alt="" /></p><p>I follow a checklist whenever I am planning to speak at a conference. I am sharing it so that hopefully it can help you too.</p><p>The steps marked with ? are the one I failed before and don't want to fail again :) It also let me justify why something that sounds silly may be part of the list!</p><p>The checklist is quite long and might seem paranoid or more stressful than helpful, but it's actually the contrary: the more you anticipate things, the less reasons you have to stress.</p><h2>Planning the talk</h2><ul><li>I plan on writing up the structure or the story before starting the slides (just like code or books, the structure must come first)</li>
<li>? I have planned to finish the first version of my slides <em>at least</em> one month before the talk (because I know that the first version will suck and will be completely rewritten)</li>
<li>I have planned to practice the talk from beginning to end at least 10 times: that means at least 10 days (once per day)</li>
</ul><h2>Writing the slides</h2><ul><li>? There is an underlying theme, story or structure in my slides: readers should not feel lost in an endless succession of unrelated slides</li>
<li>I am using a template or standard design for the slides (unless I know what I am doing)</li>
<li>? All content is <em>as big as possible</em> and will be readable from the back of the room</li>
<li>? All code is <em>as big as possible</em> and will be readable from the back of the room (yes, code needs its own line because it's the easiest thing to mess up): ideally less than 5 lines of code per slide</li>
<li>? All useless code has been removed</li>
<li>Code has syntax highlighting</li>
<li>? Unless the conference told me explicitly (with complete certainty) that the projector will be 16:9, I prepared my slides for a 4:3 format (if unsure choose 4:3)</li>
<li>I did not use animated gifs, or if I did they play only once and then they stop (there is nothing more distracting than that)</li>
<li>Less is more: the slides contain only the most important words that I am going to say, the rest will be said out loud (the audience cannot read and listen at the same time)</li>
<li>I have a slide at the beginning to introduce myself (quickly) and I try to make it relevant to the talk (in other words: "<em>Who am I and why am I here talking to you about this topic</em>")</li>
</ul><h2>Practicing the talk</h2><ul><li>? I can answer thes questions "<strong>What is the message I want to convey?</strong>" (thank you <a href="https://twitter.com/rgousi">@rgousi</a>) and "Why is that message interesting to the audience?" (thank you <a href="https://twitter.com/Louis_Remi">@Louis_Remi</a>)</li>
<li>I have practiced my talk multiple times until I feel confident</li>
<li>? I have practiced my talk without my speaker notes (to simulate the case where I cannot rely on them for some reason the day of the talk)</li>
<li>? I have practiced my talk until I consistently deliver it <em>under the allocated time</em> (being too fast is an acceptable outcome, being too long is not because organizers will stop me)</li>
<li>I can deliver my talk without reading my slides</li>
<li>I have recorded myself and listened to/watched the recording (and cringed at my voice) at least once (this is super helpful)</li>
<li>I have practiced my talk in front of somebody at least once (as early as possible), and I have asked for feedback (choose <a href="https://www.linkedin.com/in/cguilluy/">your person</a> carefully, you want brutally honest feedback here)</li>
<li>? I have practiced several times with the tool I will use to switch slides (e.g. a remote)</li>
<li>? I have practiced my talk holding a fake microphone (because it <em>will</em> be distracting when on stage)</li>
<li>? I have practiced drinking water while holding the fake microphone and the remote at the same time (this is super hard when stressed, prepare for this); practice drinking at the right time and away from the microphone if possible</li>
<li>? I know which slides to skip if I ever have to (skipping the conclusion or the most interesting parts is the worst, anticipate that)</li>
<li>? If I need to use fake names or pronouns I try to alternate genders (or use <a href="https://en.wikipedia.org/wiki/Singular_they">they</a>) so that I do not always represent developers as males</li>
<li>I have practiced to pause or change rhythm on the most important parts</li>
<li>? I know the introduction by heart (this is the most stressful moment, the brain will be turned off at that point, it needs to be easy)</li>
<li>? I know the conclusion by heart (this is what people will leave with, it needs to be as polished as possible)</li>
<li>? I end the talk with "Thank you" or a sentence that starts with those words, that is the universal signal for applause; ending with "Are there any questions?" is awkward because everyone will be unsure if they should clap or if they should stay silent for questions</li>
</ul><h2>The day before</h2><ul><li>I have stored the exact same version of my slides in 3 places: on my computer, online and on a USB key</li>
<li>? I have tested all 3 versions and made sure they work</li>
<li>? I have tested that both offline versions (computer and USB key) work <em>without internet access</em> (remember the CDN you used to load JS or fonts?)</li>
<li>I have everything I need to connect my computer to: HDMI, VGA and if necessary Ethernet (tested and working)</li>
<li>I have charged my laptop</li>
<li>I have checked that my remote is working</li>
<li>I am not planning to update my OS or any tool running on my laptop</li>
<li>If possible I have checked in the room I will be speaking in</li>
<li>? I have chosen my clothes: they are comfortable, I feel good in them and they do not show sweat marks; I am also ready to be in t-shirt or sweater: the stress can make me very hot or very cold and I do not know in advance which one it will be :)</li>
</ul><h2>Minutes before</h2><ul><li>I have gone to the bathroom (this is stupid but it's easy to forget)</li>
<li>? I have walked 10 minutes beforehand to evacuate the stress</li>
<li>? I have disabled the screen saver of my laptop and made sure it's not going to sleep</li>
<li>I have a clean desktop and neutral background picture</li>
<li>I have my remote charged and setup</li>
<li>I have all my adapters close by</li>
<li>I have my plan B ready: USB key</li>
<li>I have my plan C ready: online slides with wifi setup or my phone ready to serve as 4G access point (but keep in mind that most of the time the wifi or 4G doesn't work in conferences, that's why it's plan C)</li>
<li>? I have put my phone on "Do not disturb" or airplane mode: <strong>never on <em>vibrate</em></strong>, my pocket will vibrate because of Twitter mentions and stuff like that, and it will distract me</li>
<li>? I have enabled "Do not disturb" on my laptop to avoid popups (or disabled the wifi)</li>
<li>I have closed applications I will not need</li>
<li>I have muted my laptop (unless I need to use the audio)</li>
<li>? I have opened the slides and they are ready to roll</li>
<li>I have a bottle of water ready</li>
<li>I arrived in the room in advance (I do not attend a talk in another room right before my talk)</li>
<li>? I located the tech person or organizer, I know who to ask for help if there's a problem while setting up my laptop</li>
<li>I am ready to accept that at least 1 thing will go wrong at some point but nobody will remember or even notice</li>
</ul><h2>During the talk</h2><ul><li>? I breathe calmly</li>
<li>? I drink if I need</li>
<li>? I remember not to stress because what matters is the message, not my performance</li>
</ul><h2>After the talk</h2><ul><li>I linger a little bit after the talk and engage people that seem like they want to talk to me (unless I don't feel like it); But I don't linger in the room if there is another talk right after me, I take the discussion outside</li>
<li>? I <em>do not</em> ask people how was my talk: if people were happy with the talk they will say so, if they weren't that will either force them to lie or force them to say something they might not want (of course this may not apply to everyone)</li>
<li>I share my slides on Twitter or the Joind.in event</li>
<li>If the talked was recorded I watch the video (and cringe again) and take note of everything I can improve</li>
</ul><h2>More</h2><p>If you speak french you can read <a href="https://blog.pascal-martin.fr/tag/speaker/">the great series written by Pascal Martin on his blog</a>. It covers the topic in more details.</p></article><p></p><h2>Comments</h2>]]></summary>
    <link href="https://mnapoli.fr/speaker-checklist/"/>
    <updated>2017-11-02T10:11:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/using-non-breakable-spaces-in-test-method-names/</id>
    <title><![CDATA[Using non-breakable spaces in test method names]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">19 June 2017 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><img src="https://mnapoli.fr/images/posts/nbsp-wat.jpg" alt="" /></p><p><strong>Yes. This article is about using non-breakable spaces to name tests. And the fact that it's awesome. And why you should use them too.</strong></p><pre class="language-php">public function test a user can add a product to a wishlist()
{
    // ...
}</pre><p>The code above <a href="https://3v4l.org/MrqVL">is valid PHP code</a> and works. <a href="https://en.wikipedia.org/wiki/Non-breaking_space">Non-breaking spaces</a> (aka <code>&amp;nbsp;</code> in HTML) look like spaces in editors but are actually interpreted like any other character by PHP.</p><hr /><p>This is the kind of test that we usually see:</p><pre class="language-php">public function testAddProductToWishlist()
{
    // ...
}</pre><p>This is also the kind of method names we were writing at <a href="http://www.wizaplace.com/">Wizaplace</a> a few years ago.</p><p>At that time I was very impressed by a couple of talks which got me <a href="https://mnapoli.fr/approaching-coding-style-rationally/">interested in coding style</a>. Using <code>snake_case</code> instead of <code>camelCase</code> started to make sense in test methods because it lets us explain much more clearly what the test does:</p><pre class="language-php">public function test_a_user_can_add_a_product_to_a_wishlist()
{
    // ...
}</pre><p>(☝️ this is not PSR-2 compliant, I was dubious but yes, with time it's possible to get over it)</p><p>We ended up discussing this kind of naming in the team. Fortunately, this was also the time we were joking about <a href="https://github.com/wizaplace/thephp6framework">writing a PHP 6 framework</a> and playing with emojis as class or method names (which <a href="https://github.com/fideloper/larvel">definitely works in PHP</a>).</p><p>At some point someone trolled:</p><blockquote>
<p>if we decide to not follow PSR-2 naming for test methods because of readability, we might as well use non-breakable spaces since it's even more readable…</p>
</blockquote><p>That started as a joke but it just made sense. Since logic and humans don't always mix well together, we decided to try a <a href="http://verraes.net/2014/03/small-controlled-experiments/">small controlled experiment</a> for a while and see if it was actually great in practice.</p><pre class="language-php">public function test a user can add a product to a wishlist()
{
    // ...
}</pre><p>And it was great. So great that it has been more than a year now and we are still completely happy with it.</p><p>Test methods are clear and meaningful, here is an actual example of a diff of one of our pull request:</p><pre class="language-diff">-     public function testProjectMultiVendorProductWithOneDetached()
+     public function test product and multivendor product projections are both updated when they are detached()
      {</pre><p>Since test methods look like sentences, we think of them as sentences and it makes all tests much clearer. Here are a couple more examples:</p><pre class="language-php">public function test very long slugs are truncated()
{
    // ...
}
public function test there are no projects by default()
{
    self::assertEmpty($this-&gt;projectService-&gt;getProjects());
}</pre><h3>How to actually type a non-breaking space?</h3><p>This is very easy and quickly memorized:</p><ul><li>MacOS: <code>Alt</code>+<code>Space</code></li>
<li>Ubuntu: <code>Alt Gr</code>+<code>Space</code> (<code>Alt Gr</code> is the right <code>Alt</code> key)</li>
</ul><h3>Does it work with all the tools?</h3><p>In our experience yes, all the tools we use below work perfectly fine:</p><ul><li>git</li>
<li>PhpStorm
<ul><li>update: the shortcut will invoke the "Quick definition" helper, you will need to disable (or remap) that shortcut; "Quick definition" is also available via <code>Cmd+Y</code> or <code>Ctrl+Y</code> by default so remove the shortcut should be enough</li>
</ul></li>
<li>Sublime Text</li>
<li>GitHub (and formerly Gitlab)</li>
<li>PHPUnit's integration in PhpStorm (right-click and "Run" still works)</li>
<li>PhpStorm's analyzer and refactoring tools:</li>
</ul><p><img src="https://mnapoli.fr/images/posts/nbsp-code.png" alt="" /></p><p>We have seen minor issues on Atom and Visual Studio Code (syntax highlighting was off), those were fixed by <a href="https://twitter.com/florent_viel">Florent</a> in the following pull requests: <a href="https://github.com/atom/language-php/pull/196">atom/language-php#196</a> and <a href="https://github.com/Microsoft/vscode/pull/26992">Microsoft/vscode#26992</a>.</p><h3>Does it work with all the humans?</h3><p>This might be the most difficult part: getting other humans on board. Every new colleague that joins the team has that "WTF" moment when reading the code. That goes against the <a href="https://en.wikipedia.org/wiki/Principle_of_least_astonishment">principle of least astonishment</a>, but we are so proud and happy with non-breaking spaces that explaining it is always a fun time :)</p><p>In our experience colleagues got on board pretty quickly, both junior and senior developers. Our team is still small though, it might be more difficult to introduce this in a large organization with multiple teams.</p><h3>How can you tell if you've typed a classic space by mistake?</h3><p>Don't worry, you'll see it immediately:</p><p><img src="https://mnapoli.fr/images/posts/nbsp-error.png" alt="" /></p><p>Again, in our experience it was never an issue.</p><h3>Can it work in open source projects?</h3><p>That is the only reservation I have at the moment. Using non-breaking spaces in a closed source project where the team can own the code and share the decisions is easy: if it works, keep doing it, else stop.</p><p>In open source projects it's more complex since most contributors will get their WTF moment without you by their side to explain. It may be confusing or even off-putting.</p><p>My personal stance for now is:</p><ul><li>use this approach for small projects that will probably get no contributors</li>
<li>spread that practice as much as possible (this is what this article is about)</li>
<li>hope that it catches on to be able to use it more and more without</li>
</ul>]]></summary>
    <link href="https://mnapoli.fr/using-non-breakable-spaces-in-test-method-names/"/>
    <updated>2017-06-19T10:06:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/anonymous-classes-in-tests/</id>
    <title><![CDATA[Using anonymous classes to write simpler tests]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">15 January 2017 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><a href="https://secure.php.net/manual/en/language.oop5.anonymous.php">Anonymous classes</a> were added to PHP 7. This article intends to show how useful they can be for writing tests.</p><h2>Mocks</h2><p>Imagine the following interface:</p><pre class="language-php">interface UrlGenerator
{
    public function generateUrl($route);
}</pre><p>If you are testing a class that depends on a <code>UrlGenerator</code> implementation, you can write an anonymous class to implement this on the fly:</p><pre class="language-php">class MyTest extends TestCase
{
    public function testSomething()
    {
        $urlGenerator = new class() implements UrlGenerator {
            public function generateUrl($route)
            {
                return '';
            }
        };
        $foo = new Foo($urlGenerator);
        // ...
    }
}</pre><p>In the example above we have written a very dumb implementation. That's often useful when the mock will not be used or its behavior isn't important for the use case we are testing.</p><h2>Spies</h2><p>If we wanted to <em>spy</em> on invocations of the mock, it's trivial as well:</p><pre class="language-php">    public function test()
    {
        $urlGenerator = new class() implements UrlGenerator {
            public $routeToGenerate;
            public function generateUrl($route)
            {
                $this-&gt;routeToGenerate = $route;
                return '';
            }
        };
        $foo = new Foo($urlGenerator);
        // ...
        self::assertEquals('abcd', $urlGenerator-&gt;routeToGenerate);
    }</pre><p>Depending on the scenario, writing mocks or spies using anonymous classes can be a bit more verbose than using a mocking framework (like PHPUnit, PhpSpec, …), so it's not a silver bullet. However it's worth keeping in mind that vanilla PHP code is often easier to understand and debug than a mocking framework.</p><h2>Fixture classes</h2><p>Sometimes when writing tests, you need fixture data. But in some cases you need fixture classes. That can happen when testing tools that use reflection (I've faced that a lot with <a href="http://php-di.org/">PHP-DI</a> for example).</p><p>Writing 5-10 fixture classes is verbose, boring and more importantly it makes tests very hard to read: you have to jump between separate files to retrieve and understand the fixture classes.</p><p>Here is an example of a test relying on fixture classes:</p><pre class="language-php">class SomeDependencyInjectionContainerTest extends TestCase
{
    public function testSomething()
    {
        $container = new Container();
        $obj = $container-&gt;get(Class1::class);
        self::assertInstanceOf(Class1::class, $obj);
        self::assertNotInstanceOf(Class2::class, $obj);
    }
}
class Class1 {
}
class Class2 {
}</pre><p><code>Class1</code> and <code>Class2</code> and "fixture classes", i.e. those are classes written only for tests. You can put them inside the file containing the test class (but it's not PSR-4 compliant and it leads to conflicts if you have similar classes in other files of the same namespace), or in separate files (but then it's very hard to keep a track of which test uses which fixture class).</p><p>Here is an alternative written using anonymous classes:</p><pre class="language-php">class SomeDependencyInjectionContainerTest extends TestCase
{
    public function testSomething()
    {
        // We create 2 fake classes on the fly
        $class1 = get_class(new class() {});
        $class2 = get_class(new class() {});
        $container = new Container();
        $obj = $container-&gt;get($class1);
        self::assertInstanceOf($class1, $obj);
        self::assertNotInstanceOf($class2, $obj);
    }
}</pre><p>Don't let <code>get_class(new class() {});</code> confuse you, it simply allows us to declare a new class and get its name. It's the same as doing this:</p><pre class="language-php">class Abcd {
}
$obj = new Abcd();
$className = get_class($obj);</pre><p>With this solution fixture classes are declared inside the test, which means you only need to read the test method to understand the test entirely.</p><p>The obvious downside is that you need to understand the concept of anonymous classes, which isn't very widespread today in PHP. But like all new language feature, with time it should be more and more familiar to everyone.</p>]]></summary>
    <link href="https://mnapoli.fr/anonymous-classes-in-tests/"/>
    <updated>2017-01-15T06:01:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/approaching-coding-style-rationally/</id>
    <title><![CDATA[Approaching coding style rationally]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">12 November 2015 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Let's talk about how to format code and name things.</p><p><img src="https://mnapoli.fr/images/posts/pitchforks.jpg" alt="" /></p><p>I remember when PSR-1 and PSR-2 became a thing. Jeez that "use only spaces" thing was just ridiculous, I <em>knew</em> that tabs where better (right?). And those crazy formatting rules were the opposite of what I was used to. Nonsense! One day I did the jump and after a few weeks, I just didn't care anymore. I was over it, just like everyone else did, and <a href="http://www.php-fig.org/psr/psr-2/">PSR-2</a> was my new religion.</p><p>Fast forward a few years and I'm at a talk where the speaker argues to use <a href="https://en.wikipedia.org/wiki/Snake_case">snake case</a> for naming test methods. Dude, you crazy? We just finally got consistent style across all the PHP world! And we all know that camelCase <em>looks much better</em> right? How can I go against PSR-2? It turns out, after trying it, that this was a wonderful idea…</p><p>Habits are sometimes making us blind. We think X looks prettier than Y but that's just the habit speaking. In this article I'll try to take a rational approach at coding style. That means leaving the "it looks ugly/better" at the door.</p><p>If at any point you feel like something "just doesn't look good", breath in, breath out, and <strong>try it!</strong> Nothing beats hands-on experience, not even some random article on the internet :)</p><h2>Trailing commas?</h2><p>Should we use trailing commas or not? If you are not familiar with the practice, here is an array using trailing commas:</p><pre class="language-php">$flavors = [
   'chocolate',
   'vanilla',
];</pre><p>As you can see, the last item ends with a comma even though there is no extra item after it. It's perfectly valid PHP, the trailing comma is simply ignored. Now consider this example which is <strong>not</strong> using trailing commas:</p><pre class="language-php">$flavors = [
   'chocolate',
   'vanilla'
];</pre><p>Let's add a new value to the array:</p><pre class="language-php">$flavors = [
   'chocolate',
   'vanilla',
   'lemon'
];</pre><p>Here is the diff we generated in the commit:</p><pre class="language-diff">$flavors = [
   'chocolate',
-   'vanilla'
+   'vanilla',
+   'lemon'
];</pre><p>With trailing commas, the diff looks like this instead:</p><pre class="language-diff">$flavors = [
   'chocolate',
   'vanilla',
+   'lemon',
];</pre><p>As you can see, trailing commas lead to simpler and cleaner commits. This is also very useful when using <code>git blame</code>: each line points to the real commit that added it. Conclusion: <strong>use trailing commas</strong>.</p><p>It's interesting to note that there's currently a RFC to allow trailing commas everywhere in PHP (not just arrays). Obviously, I think it would be a great addition to the language for the reasons explained above, as well as for the sake of consistency.</p><h2>Value alignment?</h2><p>This practice is fairly common, though not universal. PSR-2 doesn't state anything about it, but many projects enforce such rule. Here is an example with phpdoc:</p><pre class="language-php">/**
 * @param int             $id       ID of the thing to export.
 * @param string          $filename Name of the file in which to export.
 * @param LoggerInterface $logger   Used to log the progress of the export.
 */</pre><p>And here is another one with arrays:</p><pre class="language-php">$formTypes = [
   'text'     =&gt; new TextField,
   'select'   =&gt; new SelectField,
   'checkbox' =&gt; new CheckboxField,
];</pre><p>Or for assignments:</p><pre class="language-php">$firstname = 'Spongebob';
$lastName  = 'Squarepants';
$age       = 25;</pre><p>Anyone who has ever modified such code knows: it's a pain. Sure it may "look good", but when modifying that you have to fill in all the extra spaces to keep the alignment. One may argue that an IDE can re-arrange that for us, but even then let's look at the diff when adding a new item to an array:</p><pre class="language-diff">$formTypes = [
-   'text'     =&gt; new TextField,
-   'select'   =&gt; new SelectField,
-   'checkbox' =&gt; new CheckboxField,
+   'text'           =&gt; new TextField,
+   'select'         =&gt; new SelectField,
+   'checkbox'       =&gt; new CheckboxField,
+   'my_custom_type' =&gt; new CheckboxField,
];</pre><p>Now all the <code>git blame</code> information is lost and the commit is almost unreadable.</p><p>Alternatively, if you don't bother keeping the alignment:</p><pre class="language-php">$formTypes = [
   'text'     =&gt; new TextField,
   'select'   =&gt; new SelectField,
   'checkbox' =&gt; new CheckboxField,
   'my_custom_type' =&gt; new CheckboxField,
];</pre><p>The whole point of the alignement is lost, and there goes consistency.</p><p>Another example following an IDE refactoring (<code>LoggerInterface</code> was renamed to <code>Logger</code>):</p><pre class="language-php">/**
 * @param int             $id       ID of the thing to export.
 * @param string          $filename Name of the file in which to export.
 * @param Logger $logger   Used to log the progress of the export.
 */</pre><p>We have all seen that!</p><p>To sum up on aligning values:</p><ul><li>it requires extra work to maintain</li>
<li>it messes up diffs and <code>git blame</code></li>
<li>it leads to inconsistent alignment over time</li>
</ul><p>Conclusion: <strong>do not align things with spaces</strong>.</p><h2>Minimal phpdoc</h2><p>Using phpdoc to document classes, functions, methods, etc. is good practice. However code considered as "well documented" usually looks like this (<a href="https://github.com/symfony/symfony/blob/v2.7.6/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php#L31-L37">on GitHub</a>):</p><pre class="language-php">/**
 * Constructor.
 *
 * @param HttpKernelInterface $kernel An HttpKernelInterface instance
 * @param string $cacheDir The cache directory (default used if null)
 */
public function __construct(HttpKernelInterface $kernel, $cacheDir = null)
{ ... }</pre><p>There is much content in this docblock, but most of it is duplicated from what developers or tools can already get from the source:</p><ul><li>we get that it's a constructor, the method is <code>__construct()</code></li>
<li>we get that <code>$kernel</code> is a <code>HttpKernelInterface</code>, the parameter is type-hinted</li>
<li>we get that a <code>HttpKernelInterface</code> type-hint means that the parameter must be "An HttpKernelInterface instance" (this comment has no added value)</li>
</ul><p>In reality here is what the docblock provides that isn't provided by the code itself:</p><ul><li><code>$cacheDir</code> is a string (or null)</li>
<li>if <code>$cacheDir</code> is null, the default cache directory will be used</li>
</ul><p>The docblock could be reduced to this:</p><pre class="language-php">/**
 * @param string|null $cacheDir The cache directory (default used if null)
 */
public function __construct(HttpKernelInterface $kernel, $cacheDir = null)
{ ... }</pre><p>On top of being information overload, the duplication also becomes a problem when the code changes. Everbody has seen a docblock that doesn't match the method it describes. When information is duplicated this is much more likely to happen.</p><p>All of this is becoming even more interesting with PHP 7 (coming with scalar type-hints and return types). Here is <a href="https://github.com/symfony/symfony/blob/v2.7.6/src/Symfony/Component/HttpKernel/KernelInterface.php#L113-L118">another example</a> to illustrate that:</p><pre class="language-php">interface KernelInterface
{
    ...
    /**
     * Gets the name of the kernel.
     *
     * @return string The kernel name
     */
    public function getName();
}</pre><p>With PHP 7 the docblock would become entirely useless:</p><pre class="language-php">interface KernelInterface
{
    ...
    public function getName() : string;
}</pre><p>Conclusion: <strong>use docblocks only to add information.</strong></p><h2>The "Interface" suffix</h2><p>This is a topic that has <a href="http://verraes.net/2013/09/sensible-interfaces/">already</a> <a href="http://phpixie.com/blog/naming-interfaces-in-php.html">been</a> <a href="https://groups.google.com/forum/#!topic/php-fig/aBUPKfTwyHo">debated</a> (the first link is the best by the way) so let's get right to the point: <strong>in most cases, there is no need to have <code>Interface</code> in the name of an interface</strong>.</p><p>Let's take that example:</p><pre class="language-php">class Foo
{
    public function __construct(CacheInterface $cache)
    { ... }
}</pre><p>One would argue here that the fact that we ask for an interface <em>explicitly</em> (it's visible through the name) is good: we know we ask for an interface. If the type-hint was for <code>Cache</code>, we wouldn't be sure whether we are asking for an interface or for an implementation.</p><p>My answer to that is that from the consumer's point of view, <strong>it doesn't matter if it's an interface or an implementation</strong>. What matters is that <code>Foo</code> needs a cache, end of story. There's a principle behind interfaces: either you have one implementation and you don't need an interface, either you have many implementations and you create an interface.</p><p>If for some reason there's only one implementation of the cache, then fine: just give me an instance of the class! If however there are multiples implementation of the cache, then <strong>each implementation exists for a specific reason</strong>. And that should be visible in the name. There is no reason there would be one implementation named <code>Cache</code>. There would be <code>RedisCache</code>, <code>FileCache</code>, <code>ArrayCache</code>, etc.</p><p>In the end, when we use the name <code>Cache</code> we either type-hint against the one implementation (no interface exists), or either against the interface. All is well!</p><p>I believe we have issues with this because :</p><ul><li>we are sometimes tempted to create interfaces when it's not needed, just because interfaces sound like bonus points towards clean code (thus leading to <code>Foo implements FooInterface</code>)</li>
<li>it requires coming up with unique names for implementations, and <a href="http://martinfowler.com/bliki/TwoHardThings.html">naming things is hard</a></li>
</ul><p>But even though this is challenging our habits, getting rid of <code>*Interface</code> forces us to think: better names and no unneeded interfaces.</p><p>For example if <code>UserRepository</code> is an interface, then you are forced to find a more specific name for your implementation. And you come up with <code>DoctrineUserRepository</code> and you realize that there could as well be <code>EloquentUserRepository</code>, <code>PdoUserRepository</code> or <code>InMemoryUserRepository</code>. <strong>Interfaces makes much more sense when they are the default</strong>. Implementations are secondary.</p><p>Let's keep in mind however that in some cases (for example in libraries/frameworks), interfaces are introduced only to allow a third party to replace the <em>default</em> implementation. We then have an interface with one implementation (other implementations are to be written by framework users). Given that context isn't the same as what was described above, it's harder to apply the same principles. In that case I can only advise to use critical thinking :)</p><h2>The "Exception" suffix</h2><p>What, again the suffixes? Yes!</p><p><em>this section is inspired from <a href="https://vimeo.com/album/2661665/video/74316116">Kevlin's mind-blowing talk</a></em></p><p>Here is where we use exceptions in PHP:</p><ul><li><code>throw ...</code></li>
<li><code>catch (...)</code></li>
<li><code>@throws</code> in phpdoc</li>
<li>when creating an exception class by extending another one</li>
</ul><p>That means that everywhere an exception class will appear, we will know it's an exception. Having the <code>Exception</code> suffix is then completely redundant.</p><p>Let's take an example: <code>UserNotFoundException</code>. The suffix brings absolutely no value. Even worse, it makes the (perfectly valid) sentence more obscure: <code>UserNotFound</code> is everything we need.</p><p>What's even more interesting is that, in some cases, removing the <code>Exception</code> suffix makes the name look quite bad. As an exercise, let's remove the suffix from <a href="https://github.com/symfony/symfony/tree/v2.7.6/src/Symfony/Component/Form/Exception">Symfony Form's exceptions</a>. By the way I'm taking examples from Symfony not because I believe it is bad code, but on the contrary because it is a very good code base (thus making the point stronger).</p><p>First let's start with the exceptions that would make perfect sense without the suffix:</p><ul><li><code>BadMethodCall</code></li>
<li><code>InvalidArgument</code></li>
<li><code>InvalidConfiguration</code></li>
<li><code>OutOfBounds</code></li>
<li><code>TransformationFailed</code></li>
<li><code>UnexpectedType</code></li>
</ul><p>Those sound like perfectly valid english sentences that explain an error.</p><p>Now let's look at those that sound more like an information than an exception or error:</p><ul><li><code>AlreadyBound</code></li>
<li><code>AlreadySubmitted</code></li>
</ul><p>Imagine that situation in real life: you want to get on the bus, but the driver tells you "no, somebody already got on". You would probably ask "So what?". The driver's problem is maybe that the bus is full. It's the same here: maybe a form cannot be re-bound if it has already been bound.</p><p>How about <code>CannotRebind</code> and <code>CannotSubmitAgain</code> as exception names instead?</p><p>Lastly, let's look at the exceptions left:</p><ul><li><code>ErrorMappingException</code> (notice the combo error + exception)</li>
<li><code>LogicException</code>: exception/error in the logic</li>
<li><code>RuntimeException</code>: exception/error at runtime</li>
<li><code>StringCastException</code>: exception/error while casting to string?</li>
</ul><p>Except <code>RuntimeException</code> and <code>LogicException</code> which are very generic exceptions (maybe they deserve to be more specific?), here are suggestions for the other two:</p><ul><li><code>InvalidMapping</code></li>
<li><code>CannotBeCastToString</code></li>
</ul><p>Not convinced? Have a look at your own code and do the same exercise, it might give you a new persepective on your errors.</p><p>Conclusion: <strong>the <code>Exception</code> suffix is unnecessary</strong>. Not using it also has the benefit of forcing us to come up with names that better describe an actual error.</p><h2>Conclusion</h2><p>In the end we only had a look at 5 actual examples, but I want to stress that the main point of this article is:</p><ul><li>it's possible to think about coding style logically</li>
<li>sometimes doing so forces us to challenge our habits</li>
<li>when unsure or dubious: just try</li>
</ul><p>If you need to vent off on how some of this is stupid and ugly, there's a comment box below. I would also be happy to hear about practices you tried and you liked!</p>]]></summary>
    <link href="https://mnapoli.fr/approaching-coding-style-rationally/"/>
    <updated>2015-11-12T06:11:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/silly-cli/</id>
    <title><![CDATA[Silly CLI 1.1 comes with dependency injection]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">12 April 2015 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>I have just released <a href="http://mnapoli.fr/silly/">Silly CLI</a> 1.1 and I think it's awesome, here's why.</p><h2>What is Silly?</h2><p>If you missed it, <a href="http://mnapoli.fr/silly/">Silly</a> is a small project I started a month ago. The idea is simple: <em>it is a CLI micro-framework</em>.</p><p>Much like Silex or Slim let you create a web application in a single file using closures as controllers, Silly let you create a CLI application in a single file using closures as commands:</p><pre class="language-php">$app = new Silly\Application();
$app-&gt;command('greet [name] [--yell]', function ($name, $yell, $output) {
    $text = $name ? 'Hello '.$name : 'Hello';
    if ($yell) {
        $text = strtoupper($text);
    }
    $output-&gt;writeln($text);
});
$app-&gt;run();</pre><p>As you can see, no need for classes. Also, commands are defined using a syntax inspired from help and man pages which is short and reads nicely.</p><p>Running the application:</p><pre>$ php app.php greet
Hello
$ php app.php greet john --yell
HELLO JOHN</pre><p>Of course I didn't reinvent anything. All this is based on <a href="http://symfony.com/fr/doc/current/components/console/introduction.html">Symfony Console</a>, which is great because:</p><ul><li>I'm lazy</li>
<li>everybody knows the Symfony Console</li>
</ul><p>This was Silly CLI 1.0, and it was good.</p><h2>Scaling up</h2><p>I like micro-frameworks because they allow us to start very simply and very quickly. They also generally don't force us into a specific architecture or organization which, sometimes, is good.</p><p>But the value of a micro-framework is even greater when it allows you to <strong>scale up</strong>. By that I don't mean "mongodb web-scale", I mean "my single script file is too big, I want to organize everything more properly and start doing complex stuff".</p><h3>Callables</h3><p>This is where a immensely useful concept of PHP comes into play: <strong>callables</strong>.</p><p>Most examples of micro-frameworks show you the "closure" example of callables because it's the most obvious one for a simple app:</p><pre class="language-php">$app-&gt;command('greet [name]', function ($name) {
    // ...
});</pre><p>But as you probably know, closures are not the only kind of <em>callables</em> in PHP. Here is a list of valid PHP callables:</p><ul><li>a closure: <code>function () {}</code></li>
<li>a function name: <code>'strlen'</code></li>
<li>an object method: <code>[$object, 'theMethod']</code></li>
<li>a class static method: <code>['TheClass', 'theMethod']</code></li>
<li>invokable object: <code>$object</code> (an object that has an <code>__invoke()</code> method)</li>
</ul><p>All that means is we can replace our closure with classes. Our simple example can then become:</p><pre class="language-php">class GreetCommand
{
    public function execute($name)
    {
        // ...
    }
}
$app-&gt;command('greet [name]', [new GreetCommand, 'execute']);</pre><p>That's a nice first step to organize our growing application. Each command can be put in its own class and file.</p><h2>Silly 1.1: Dependency injection</h2><p>Putting our commands in separate files is a good start. But everyone knows that a growing application is not just about organizing code: it's also about managing dependencies.</p><p>Instead of turning towards singletons or global functions/static methods, Silly 1.1 helps you into using dependency injection. More specifically, it comes with <strong>dependency injection container support</strong>.</p><h3>Choose your container</h3><p>Silex, Slim, Cilex, … all force you into using a specific container (often Pimple). While this is practical at first, it's usually not so good for <em>scaling up</em>: the more classes or dependencies you have, the more configuration you will write.</p><p>Instead of forcing you into using a <em>supposedly</em> better DI container, Silly goes <a href="https://github.com/container-interop/container-interop">the interoperability route</a>: <strong>use whichever container you want!</strong></p><pre class="language-php">$app-&gt;useContainer($container);</pre><p>The container you provide has to be compliant with the <a href="https://github.com/container-interop/container-interop">container-interop</a> standard. If it isn't you can use <a href="https://github.com/jeremeamia/acclimate-container">Acclimate</a>.</p><h3>Callables in the container</h3><p>By simply registering a container with <code>-&gt;useContainer()</code>, Silly will retrieve commands from the container when they are not PHP callables:</p><pre class="language-php">// Valid PHP callables: doesn't use the container
$app-&gt;command('greet [name]', function () { /* ... */ });
$app-&gt;command('greet [name]', [new GreetCommand, 'execute']);
$app-&gt;command('greet [name]', ['SomeClass', 'someStaticMethod']);
$app-&gt;command('greet [name]', new InvokableClassCommand);
// Use the container
$app-&gt;command('greet [name]', ['GreetCommand', 'execute']); // execute is not a static method
$app-&gt;command('greet [name]', 'InvokableClassCommand');     // implements __invoke()</pre><p>In the 2 last examples above, the container will be called to instantiate <code>GreetCommand</code> and <code>InvokableClassCommand</code>.</p><p>That means that you can configure those classes in your container and benefit from the dependency injection features of your container. For example:</p><pre class="language-php">class GreetCommand
{
    private $entityManager;
    public function __construct(EntityManager $entityManager)
    {
        $this-&gt;entityManager = $entityManager;
    }
    public function execute($name)
    {
        // ...
    }
}</pre><p>Note that our class could also be an invokable class (i.e. one that <a href="http://php.net/manual/en/language.oop5.magic.php#object.invoke">implements <code>__invoke()</code></a>).</p><h3>Dependency injection in parameters</h3><p>Dependency injection as shown above only works when you write commands in classes. However you can't use it with closures!</p><p>To solve that problem, let's have a look at how dependency injection is performed in AngularJS:</p><pre class="language-js">angular.controller('MyController', function (myService, myOtherService) {
  // ...
});</pre><p>Silly supports the same mechanism of dependency injection through the callable parameters. For example:</p><pre class="language-php">use Psr\Logger\LoggerInterface;
// the order of parameters doesn't matter:
$app-&gt;command('process [directory]', function (LoggerInterface $logger, $directory) {
    $logger-&gt;info('Processing directory ' . $directory);
    // ...
});</pre><p>Silly can inject services and values into parameters by looking into the container using:</p><ul><li>the <strong>type-hint</strong> (i.e. the interface/class name): <code>Psr\Logger\LoggerInterface</code></li>
<li>the <strong>parameter's name</strong>: <code>logger</code></li>
</ul><p>Depending on how you declare your container entries you might want to enable one or the other way, or both.</p><pre class="language-php">$app-&gt;useContainer($container, $injectByTypeHint = true, $injectByParameterName = true);</pre><p>If you set both to <code>true</code>, it will first look using the type-hint, then using the parameter name.</p><h2>The PHP-DI and Pimple edition</h2><p>Being able to use any container is nice, but sometimes you don't really care and just want to get started. You are covered!</p><p>I have created two packages that provide you Silly pre-configured with either <a href="http://php-di.org">PHP-DI</a> or <a href="http://pimple.sensiolabs.org/">Pimple</a>.</p><p>Those wanting the benefit of autowiring might like the former, while those familiar with Silex might feel more at ease with the latter.</p><h2>Beyond Silly</h2><p>I hope this example will show how a framework can be built without coupling to a dependency injection container. This was rather easy because it is a micro-framework, but I hope it will give others some ideas.</p><p>For those interested to learn more about these topics, everything explained here was actually implemented in a library called <a href="https://github.com/mnapoli/Invoker">Invoker</a>. Feel free to have a look at it and maybe use it in your projects. The same principles shown here could be applied to any framework dispatching to PHP callables. Hopefully it will catch on!</p><p><em>Thanks for <a href="https://twitter.com/Darhazer">@Darhazer</a> and <a href="https://twitter.com/najidev">@najidev</a> for reviewing this article.</em></p>]]></summary>
    <link href="https://mnapoli.fr/silly-cli/"/>
    <updated>2015-04-12T06:04:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/test-lowest-dependencies/</id>
    <title><![CDATA[Test against the lowest Composer dependencies on Travis]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">17 December 2014 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Composer just got a <a href="https://github.com/composer/composer/pull/3450">new awesome addition</a> thanks to <a href="https://twitter.com/nicolasgrekas">Nicolas Grekas</a>: prefer the lowest versions of your dependencies.</p><pre>composer update --prefer-lowest</pre><p>This amazing option will install the lowest versions possible for all your dependencies.</p><p>What for? <strong>Tests</strong> of course!</p><h2>Update Composer</h2><p>OK before we get started, don't forget to update Composer right now if you want to use the new option:</p><pre>sudo composer self-update</pre><h2>Testing against lowest dependencies</h2><p>It might not make a lot of sense to test an <strong>application</strong> against its lowest dependencies, because unless something is done wrong it will be installed with controlled versions.</p><p>However, for <strong>libraries</strong> (or <em>components</em>…) it's a very good thing.</p><p>To give you an example, I wanted to give it a try and ran <code>composer update --prefer-lowest</code> in <a href="http://php-di.org">PHP-DI</a>'s directory: all hell broke loose… PHPUnit wouldn't even start.</p><p>The reason for this were multiples, but they all boiled down to:</p><ul><li>I used a feature that didn't yet exist in version X of dependency Y</li>
<li>or dependency Y had a bug in version X</li>
</ul><p><strong>and I allowed installing that problematic version X in <code>composer.json</code></strong>.</p><p>In the end, I was distributing a library that <em>could</em> not work at all (if people had insane version constraints).</p><h2>Setting up tests on Travis</h2><p>Let's be proactive and use continuous integration to prevent that from happening again.</p><p>Here is an example of a <code>.travis.yml</code> configuration that would run the tests using the lowest dependencies with PHP 5.3.3:</p><pre class="language-yml">language: php
php:
  - 5.3.3
  - 5.3
  - 5.4
  - 5.5
  - 5.6
  - hhvm
matrix:
  include:
    - php: 5.3.3
      env: dependencies=lowest
before_script:
  - composer self-update
  - composer install -n
  - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --prefer-stable -n; fi;
script:
  - phpunit</pre><p>You'll notice that we have to run <code>composer self-update</code> because Travis hasn't picked up the latest Composer version yet.</p><p>Here was the result on Travis:</p><p><img src="https://mnapoli.fr/images/posts/composer-lowest-dependencies.png" alt="" /></p><p>A new job was added to the build matrix, and this job ran on PHP 5.3.3 with the lowest dependency versions.</p><p>If you are interested, have a look at <a href="https://github.com/mnapoli/PHP-DI/pull/219">the pull request</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/test-lowest-dependencies/"/>
    <updated>2014-12-17T06:12:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/decoupling-packages/</id>
    <title><![CDATA[Decoupling packages]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">21 September 2014 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Decoupling packages is a hard thing. There are not a lot of options, and this blog post is about how some options are better than others.</p><p>Let's say for example that you are writing a "package", or library, to respond to HTTP requests (that kind of package could be considered the basis for a web framework). How do you handle routing?</p><p>If you write your Router package as an independent package (which is good: small and specialized packages are more reusable and maintainable), you might not want to couple the HTTP package to the Router package: you want to leave users free to choose the router of their choice.</p><p>So, what are your options to make the HTTP package and the Router package decoupled from each other?</p><h2>Events</h2><p>The first option is to turn to <strong>event-driven programming</strong>. By relying on an "event manager" library/package, you can have your package raise specific events at strategic points in your code. And you can have those events affect the code flow.</p><p>This is the solution chosen by Symfony to solve the routing problem exposed earlier. Their HttpKernel component is decoupled form their Routing component through events.</p><p>Here is the simplified code flow of their <code>HttpKernel</code> class/component (which is the basis for an HTTP application):</p><pre class="language-php">$eventDispatcher-&gt;dispatch(
    KernelEvents::REQUEST,
    new GetResponseEvent($this, $request, $type)
);
$controller = $request-&gt;attributes-&gt;get('_controller');
if (! $controller) {
    throw new NotFoundHttpException();
}</pre><p>So here is what's happening:</p><ul><li>the <code>HttpKernel</code> raises a "Kernel Request" event</li>
<li>then <strong>it expects</strong> that a listener set the controller under the "_controller" key in the request</li>
</ul><p>So is <code>HttpKernel</code> decoupled from the <code>Router</code> component? <strong>Yes</strong>. The listener that sets the controller could actually be anything.</p><p>The problem is, as emphasized above, that the <code>HttpKernel</code> <strong>expects something very specific from the listeners</strong>. The whole application depends on an unknown listener being actually registered for that particular event and following an exact, <strong>unspecified</strong> behavior.</p><p>I believe events should be used for hooking up in the main logic flow to <em>extend it</em>. <strong>But the main logic flow should be linear and not rely on the possible <em>side effects</em> of an event.</strong></p><p>There are some other problems we can find with this solution:</p><ul><li>the package ends up coupled to the "event manager" package (you just replaced a dependency by another)</li>
<li>the code is not linear: it makes it much harder for developers to put the pieces back together and contribute to the project</li>
<li>behavior from decoupled packages <strong>are not specified by contracts</strong></li>
</ul><p>Regarding the pros:</p><ul><li>the behavior is <em>not specified</em>, which can also be a good thing: it leaves every possible option open for the future</li>
<li>might be simpler than the other options</li>
</ul><p><code>&lt;disclaimer&gt;</code>Just to be clear, Symfony is an exceptional framework and I love it. I am sure the decision to choose this option was carefully thought through, and the important thing to remember is that <strong>it works</strong>. Symfony is probably the most popular modern PHP framework, and my blog post doesn't change that. I am just using it as an example here, I just want to expose alternative options and discuss the pros and cons.<code>&lt;/disclaimer&gt;</code></p><h2>Interfaces and adapters</h2><p>I was mentioning <em>specifying behaviors</em> with <strong>contracts</strong>. In PHP (and most OO languages), you can implement this using <strong>interfaces</strong>.</p><p>If we take the previous example, an HTTP package could contain a <code>RouterInterface</code> to specify how a routing component should behave (this is a very basic example):</p><pre class="language-php">namespace Acme\Http;
interface RouterInterface {
    /**
     * @return callable The controller to use for this request
     */
    public function route(Request $request);
}</pre><p>You'll notice that not everything is specifiable, e.g. the return types. Hopefully PHP will allow that in its next major version, but until then the only solution is to use documentation.</p><p>In the <code>HttpApplication</code> class, we can use it in type-hints to accept any implementation (classic dependency injection here). And here is what the code logic would look like:</p><pre class="language-php">$controller = $router-&gt;route($request);
if (! $controller) {
    throw new NotFoundHttpException();
}</pre><p><strong>The code flow is linear and completely explicit.</strong> And the HTTP package is decoupled from any Router package!</p><p>The only problem left: Router packages would be forced to implement <code>Acme\Http\RouterInterface</code> if they want to be used with the HTTP package. Because of that, they end up coupled to it… So how do we decouple Router packages from the HTTP package?</p><p>A way to go around that is to use <strong>adapters</strong>:</p><pre class="language-php">namespace \MyApplication\Adapter;
class HttpRouterInterfaceAdapter implements \Acme\Http\RouterInterface {
    private $router;
    public function __construct(\Acme\Router\Router $router) {
        $this-&gt;router = $router;
    }
    public function route(Request $request) {
          $this-&gt;router-&gt;route($request);
    }
}</pre><p>Thanks to that adapter, the <code>Router</code> class doesn't need to implement the <code>RouterInterface</code>. So it is completely decoupled from the HTTP package.</p><p>However, as you can see, it requires writing an adapter each time you want to "connect" decoupled packages.</p><p>Pros:</p><ul><li>linear and explicit code flow</li>
<li>specified behavior (using interfaces)</li>
</ul><p>Cons:</p><ul><li>requires to write interfaces</li>
<li>requires to write adapters</li>
</ul><p>This strategy of interfaces was recently used by <strong>Laravel</strong>. For the version 5.0 (IIRC), Laravel will publish a package named <a href="https://github.com/illuminate/contracts"><code>illuminate/contracts</code></a> which contains all the interfaces used by its other packages.</p><p>That allows to have decoupled Laravel packages while not needing adapters to use them together: packages can implement the interfaces at the very small cost of being coupled to <code>illuminate/contracts</code> (it's a small cost because the package is very light and contains only interfaces).</p><h2>Standardized interfaces</h2><p>Now the last option is to go a step beyond and try to make the interfaces "<strong>standard</strong>". By that I mean that the same interface would be used by many packages, and implemented by many others.</p><p>The good example for this is obviously <strong>logging</strong>. There used to be a numerous amount of different logger libraries for PHP. Then the <a href="http://www.php-fig.org/">PHP-FIG</a> group worked to produce <a href="https://github.com/php-fig/log">PSR-3</a>, the logger standard.</p><p>Today, many logging packages implement the <code>Psr\Log\LoggerInterface</code>, and most modern frameworks type-hint against that interface instead of specific implementations. That means that users can choose any PSR-3 compliant logger and have their framework use it.</p><p>Needless to say that this is an ideal situation: <strong>no coupling, no effort</strong>! But logging was kind of an easy topic. It's very hard to come up with standards for all the other components that need interfaces, mainly because implementations often differ a lot.</p><p>The <a href="http://www.php-fig.org/">PHP-FIG</a> has been working for a few years on a Cache and an HTTP message PSR, and hopefully they will be released sometime. In the meantime, the <a href="https://github.com/container-interop/container-interop">container-interop project</a> aims at providing interfaces to standardize the usage of dependency injection containers.</p><h2>Conclusion</h2><p>OK, there's not much left to add here, if you have any reaction about this I'd be happy to hear it. If I got anything wrong, I'd be happy to correct it.</p><p>I would like to finish on an idea that was suggested about a year ago on the PHP internals mailing list: "<strong>weak interfaces</strong>". Those are interfaces that define a behavior, but <strong>that do not need to be implemented by classes</strong>. It mixes the principle of static-typing with duck-typing:</p><blockquote>
<p>If it looks like a duck and quacks like a duck, then it's a duck.</p>
</blockquote><p>What's really good with this is that it allows packages to define their interfaces, and type-hint against it, all the while not requiring dependencies to actually implement it. As long as objects are compatible with the interface, it works. It's a sort of <code>class X implements Y</code> evaluated at runtime. Example:</p><pre class="language-php">interface FooInterface {
    public function hello();
}
// Foo does not implement FooInterface
class Foo {
    // But this method makes it compatible with FooInterface
    public function hello()
    {
        return 'Hello world';
    }
}
// That pseudo-syntax tells that this is a weak-interface type-hinting
function run(&lt;&lt;FooInterface&gt;&gt; $foo) {
    echo $foo;
}
// It works because Foo is compatible with the interface
run(new Foo);
// Error, stdClass is not compatible with FooInterface
run(new stdClass);</pre><p>This example was just for fun, but I wish such a feature would land in PHP (along with static return type). It would help a lot with package interoperability and decoupling.</p><p><strong>Update:</strong> the original RFC about what I called <em>weak interfaces</em> has been linked to me by its author, Anthony Ferrara. Here it is: <a href="https://wiki.php.net/rfc/protocol_type_hinting">PHP RFC: Structural Type Hinting</a>. As you can see, it was named <em>Structural type hinting</em> instead of <em>weak interface</em>.</p><p>It has also been pointed to me in the comments that the same Anthony Ferrara wrote a userland implementation of this: <a href="https://github.com/ircmaxell/Protocol-Lib">ProtocolLib</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/decoupling-packages/"/>
    <updated>2014-09-21T11:09:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/retrospective-mycsense-open-source/</id>
    <title><![CDATA[A retrospective on open sourcing projects at My C-Sense]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">1 July 2014 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Today is my last day at <a href="http://www.myc-sense.com"><strong>My C-Sense</strong></a>, after almost 2 years working on creating and modernizing existing PHP applications. This has been an incredibly rich experience in an excellent work environment, I will surely miss this job and my colleagues.</p><p>Now is probably a good time to look back on our open source experience as a company, and as a developer. It all started when we moved all of our code to GitHub: we used a lot of open source software and libraries, and we thought we might try to contribute some day with some small internal projects too.</p><p>So that's how <a href="https://github.com/myclabs"><strong>MyCLabs</strong></a> was born: let's keep the door open for open source because "why not".</p><h2>jquery.confirm</h2><p>The first project was born out of a simple need: a confirmation box on some buttons and links. We were using Bootstrap, but adding all the JS and HTML needed for such a simple thing was definitely worth a JS function. So instead of a quick and dirty solution, I turned to jQuery and learned how plugins worked, just for fun and because it wasn't a big investment. In no time, <a href="https://github.com/myclabs/jquery.confirm"><strong>jquery.confirm</strong></a> was born (the <a href="http://myclabs.github.io/jquery.confirm">demo is here</a>).</p><p>The project was absolutely not ambitious, yet it's probably our most successful project yet. It has <em>30 forks</em>, received 10 pull requests and has 13 releases. That's not much, but for a simple confirmation dialog, it's quite nice.</p><p>And what's interesting is that it's the perfect example of an open source "success" from the company's point of view: we receive much more contributions than we contribute to it ourselves. We get bugfixes "for free", and we even got our migration to Bootstrap 3 covered for us <a href="https://github.com/myclabs/jquery.confirm/pull/10">by a contributor</a>. Which company wouldn't want that?</p><h2>PHP Enum</h2><p>The next project open sourced was a bit different. <a href="https://github.com/myclabs/php-enum"><strong>PHP Enum</strong></a> is an implementation of an Enum structure for PHP. That's probably not the first time you hear about something like that, so here is why we created one:</p><ul><li><a href="http://php.net/manual/en/class.splenum.php">SplEnum</a> seems like a good solution, but it is actually a separate PHP extension that needs to be installed (which makes it useless IMO)</li>
<li>the other open source implementations were not good enough: either they were too far from SplEnum (which means most of the time they were working in a weird way), either they were a direct clone of SplEnum and there was a bit of room for improvement. If you do a search today, you'll find other good libraries that were different or non-existant at the time</li>
</ul><p>We ended up using it in some places in our application, and I ended up using it in <a href="http://php-di.org">PHP-DI</a>. We received a few contributions, but obviously there's not a lot of room for bugs or new features since it's very simple.</p><p>In the end, this library has been installed <em>more than 16 000 times</em>, which is definitely a lot and that makes us happy (PHP-DI installs might represent a good share of that).</p><h2>MyCLabs\ACL</h2><p><a href="http://myclabs.github.io/ACL/"><strong>MyCLabs\ACL</strong></a> is the one I'm the most proud of. We have been working on <em>the ACL problem</em> and <strong>access control</strong> for more than 4 years (I've been on and off at My C-Sense), changing, improving and rewriting our solution about every year. It has been a major headache, especially since it's a problem that leaks everywhere: in the model, at the database level, at the routing level, in the views, … And its impact on performance was definitely one of the biggest challenge.</p><p>We were always looking at all the existing PHP solutions, but none of them was ever <em>even close</em> to fit our need. We've always been amazed about this, because we are sure many other companies have the same needs as us.</p><p>So because of all this, our ACL system was something I definitely wanted to open source some day. But given its complexity, and the fact it was deeply coupled to our application, it was impossible. Then came Doctrine 2.4, and then 2.5, and it changed a lot of things. As the next big rewrite (again) was planned, I suggested we tried to make it open source. And that would not mean "just publish the code online". That would mean make the effort on making a completely decoupled library with tests and documentation.</p><p>It took a few weeks to build it, and the company paid for that effort. But it was greatly rewarded with a solution much more solid and powerful than we ever built.</p><p><strong>Thinking and building our code on in a more generic way was definitely beneficial.</strong> We previously had an unstable, half-tested, undocumented, slow and unmaintainable ACL system. We wouldn't dare touch it in fear of breaking everything. We now have a very powerful, tested, fast and documented solution. Yes it took time, but it was definitely worth it.</p><p>And I think this library is a perfect example of how <strong>open source was beneficial, even without any external contribution</strong>.</p><p>And as a developer, making the library open source turned the <em>most boring problem on earth</em> (permissions!) into a fascinating one! I couldn't stop thinking about it out of work when I was working on it.</p><p>My C-Sense is now working on a <a href="https://github.com/myclabs/ACL/pull/9">v2</a>, with a much more simpler configuration and usage (much less boilerplate code). That's so interesting that I'll probably be following the project and maybe helping out if I have the time ;)</p><h2>MyCLabs\Work</h2><p>The ACL project had been a success, so when we ended up moving away from Gearman and thus rewriting our worker system (coupling, coupling…), I suggested we tried building an open source solution for it. Here's <a href="http://myclabs.github.io/Work/"><strong>MyCLabs\Work</strong></a>.</p><p>Of course, I had a look at everything that could fit the bill. But again, we needed a feature that wasn't provided by any open source library I could find:</p><blockquote>
<p>That task is too long, it needs to run in background and the user will be notified by email when it finishes. But sometimes it can be very fast, so receiving an email is useless and looks stupid.</p>
<p>So we want to run the task in background, but if it takes less than 5 seconds, we need to show the result to the user directly (in the web page) as if it didn't run in background. And of course, the worker shouldn't send any email!</p>
</blockquote><p>That looks like a very simple need from a user's point of view. But when you realize that it means you must have a <strong>bidirectional communication</strong> between the web page and the background worker, you start going "oh no no no".</p><p>Now we just ditched Gearman (because of too many bugs and installation problems), and we didn't want to tie ourselves again to a different work queue. So our library was built against 2 needs:</p><ul><li>an abstraction over different queue systems (RabbitMQ, Beanstalkd, InMemory…)</li>
<li>an abstraction over the "run and wait if it finishes else …"</li>
</ul><p>Of course, the <code>run and wait</code> wasn't possible on every queue system, so it needed to be something optional. So I ended up writing 2 interfaces:</p><pre class="language-php">interface WorkDispatcher
{
    public function run(Task $task);
}
interface SynchronousWorkDispatcher extends WorkDispatcher
{
    public function runAndWait(
        Task $task,
        $wait = 0, // time to wait, in seconds
        callable $completed = null,
        callable $timedout = null,
        callable $errored = null
    );
}</pre><p>(the details are documented <a href="http://myclabs.github.io/Work/">here</a>)</p><p>For example, the Beanstalkd adapter doesn't implement <code>SynchronousWorkDispatcher</code>, whereas the RabbitMQ adapter does.</p><p>Now where it begins to be interesting is that there is no queue system that provides out of the box bidirectional communication with a worker (at least in the one I reviewed at that time, the one with a good PHP lib and the one that can be installed). After several days of thinking and trials, I managed to implement the <code>runAndWait</code> behavior in RabbitMQ with the use of a temp queues and some sort of high level "protocol". Maybe not the fastest solution on earth, but for our needs it wasn't a problem at all. If you are curious, you can checkout <a href="https://github.com/myclabs/Work/tree/master/src/Adapter/RabbitMQ">here</a> how it is implemented.</p><p>In the end, this is really was this library does: it <strong>abstract</strong> 2 behaviors: simply run a task in background, and run a task and wait for its result.</p><p>MyCLabs\Work now has adapters for RabbitMQ, Beanstalkd and "In Memory" (i.e. for your development machine), you are welcome to push new adapters through pull requests. It's very simple if you don't implement the <code>SynchronousWorkDispatcher</code> interface, for example <a href="https://github.com/myclabs/Work/tree/master/src/Adapter/Beanstalkd">look at the Beanstalkd adapter</a>.</p><h2>DeepCopy</h2><p><a href="https://github.com/myclabs/DeepCopy"><strong>DeepCopy</strong></a> is a small utility that helps you create deep copies of objects. By deep copy, I mean when you want to duplicate/clone an object <em>and it's sub-objects</em>.</p><p>Imagine you need to provide the user a way to "duplicate a folder". The user will expect all the files and subfolders to be duplicated too. PHP's <code>clone</code> will do a shallow copy, which means it will only clone the root object, the properties of the cloned object will reference the same objects as the original object.</p><p>The "built-in" PHP solution would be to override <code>__clone()</code>, but that leads to quite complex code, and handling cycling dependencies is very hard.</p><p>DeepCopy will handle all that for you. And on top of it, it's completely configurable: you can skip properties, force them to keep their original value, or set them to <code>null</code>. DeepCopy also supports resolving Doctrine's collections and proxies.</p><h2>ArrayComparator</h2><p><a href="https://github.com/myclabs/ArrayComparator"><strong>ArrayComparator</strong></a> is a bit like DeepCopy: it's a small utility for comparing arrays containing objects. It let's you define callbacks that will be called when items are different or missing between arrays.</p><h2>Contributions to other open source projects</h2><p>While we use the <a href="https://github.com/myclabs">MyCLabs</a> organization to publish open source libraries, we also use it to host forks of other libraries. That had become quite handy when we proposed a bugfix for Doctrine: let's install our fork instead of the original repo. By the way, <a href="http://mnapoli.fr/overriding-dependencies-with-composer/">here is how to do it with Composer</a>.</p><p>So up to now, we contributed mostly to <a href="http://doctrine-project.org">Doctrine</a>, the <a href="https://github.com/Atlantic18/DoctrineExtensions">DoctrineExtensions</a> and <a href="https://github.com/PHPOffice/PHPExcel">PHPExcel</a>.</p><h2>A word on documentation</h2><p><em>This part is a bit of self-promotion :)</em></p><p>We ended up having 2 kinds of projects:</p><ul><li>small projects where the GitHub project and a single Readme would be enough</li>
<li>larger projects with multi-pages documentation</li>
</ul><p>For larger projects, we have been using <a href="http://mnapoli.fr/Couscous/"><strong>Couscous</strong></a> to generate the websites from the Markdown documentation.</p><p>It is working pretty well, the local preview is useful and deploying is very simple. And with the newly implemented templates it proved to be even more useful: I have written a <a href="https://github.com/myclabs/couscous-template">Couscous template for MyCLabs projects</a>, and I use it for <a href="http://myclabs.github.io/ACL/">MyCLabs\ACL</a> and MyCLabs\Work.</p><p>If you too want to use GitHub Pages to publish a website based on your documentation, give it a try.</p><h2>Conclusion</h2><p>This was a big list of most of My C-Sense's open source projects. There are even some more that I didn't mention (because the article is getting quite long), so check out <a href="https://github.com/myclabs">MyCLabs page</a>.</p><p>To conclude on the open source experience <em>from the company's point of view</em> (and this is my own opinion here), I can only find it beneficial.</p><p>Of course, don't expect to get contributions right away and have your software developed for you. Out of all the project we open sourced, we only got a dozen of pull requests. And you have to realize that it takes time to manage them, and the issues (which sometimes are just people asking for help). Remember than an open source project not maintained is hurting the community rather than helping it.</p><p>That being said, there are many advantages:</p><ul><li>an open source library usually mean <strong>better quality</strong>, because that code is public. I would much more restrain from committing a dirty hack if I know the code will be online for years :p But mainly it really help the developer to take a step back and really think about what the library is supposed to do in a more generic way, and also to be more open to other implementations.</li>
<li>a company involved in open source projects looks sexy to developers. <strong>Recruiting</strong> is (I presume) easier, and also it allows to attract and recruit good developers: they know what kind of code and quality level to expect, just like the recruiter does when he browses GitHub profiles of candidates.</li>
<li>open sourced code get fixes and improvements… sometimes. And for free… almost (if you consider the time needed to merge/handle the tickets)</li>
<li><strong>developers do a better work</strong>: publishing an open source project online, or contributing to one, is a great experience. You contribute for something more timeless than your job. The code you write might be used by your peers across the world for year! That's challenging and exciting.</li>
</ul><p>The last point is also about the developer's point of view: working on open source projects is an additional source of motivation and involvement. And of course it benefits everyone.</p><p>So, companies: <em>as long as you don't make money over it, think about open sourcing it?</em></p><p>Now I will finish with a very warm thank you to My C-Sense and its founders for trusting us and letting us try. Open source has had a very positive result up to now.</p>]]></summary>
    <link href="https://mnapoli.fr/retrospective-mycsense-open-source/"/>
    <updated>2014-07-01T06:07:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/collection-interface-and-database-abstraction/</id>
    <title><![CDATA[The Collection interface and Database abstraction]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">27 March 2014 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>This article logically follows the previous: <a href="https://mnapoli.fr/repository-interface/">The Repository interface</a>. In there I suggested a better interface for repositories based on a Collection interface so that repositories could be manipulated like collections.</p><p>This article goes further in that idea and presents an API to abstract database access behind collections of objects.</p><p>But first let's start with Doctrine Criterias.</p><h2>What are Doctrine Criterias?</h2><p>When you use Doctrine, you end up interacting with list of objects in two ways:</p><ul><li>through repositories or the entity manager</li>
<li>through associations</li>
</ul><p>At the entity manager or repository level, you can write <strong>DQL queries</strong> (or use the query builder that will generate them for you). This is the most powerful API Doctrine provides to interact with the database.</p><p>But inside objects you don't have access to the entity manager or the repository, because the model shouldn't know about the persistence. So what do you do when you want to <strong>filter an association without loading all the objects?</strong> And furthermore: what do you do <strong>if the association is already loaded in memory?</strong> That would be stupid to issue a DB query when you could just filter the objects in memory.</p><p>That's where Doctrine introduced the concept of <strong>Criteria</strong>. It's an API that allows you to filter an association with an abstract "query" (≠ DB query) that you apply to a collection. What's good is:</p><ul><li>if the collection is already loaded, the filtering happens in memory with PHP</li>
<li>if the collection is not loaded, the filtering is done in a DB query</li>
</ul><blockquote>
<p>The Criteria has a limited matching language that works both on the SQL and on the PHP collection level. This means you can use collection matching interchangeably, independent of in-memory or sql-backed collections.</p>
</blockquote><p>That's awesome!</p><p>Example of how to use it:</p><pre class="language-php">$criteria = Criteria::create()
    -&gt;where(Criteria::expr()-&gt;eq('birthday', '1982-02-17'))
    -&gt;orderBy(array('username' =&gt; Criteria::ASC))
    -&gt;setFirstResult(0)
    -&gt;setMaxResults(20);
$birthdayUsers = $userCollection-&gt;matching($criteria);</pre><p>The Criteria API is obviously much more limited than DQL, but <strong>it's completely decoupled from persistence</strong>: you can use it in your model without polluting it with persistence problems, and you can use it against collections, loaded or not.</p><p>And the greatest thing of all: Doctrine developers didn't stop here, they also made the Repositories be compatible with those Criterias. Which means the same API whether it's a repository or not. If you read my previous article, you know how much I like that.</p><h2>What's wrong then?</h2><p>Well something has to be wrong else I wouldn't be writing this, and you wouldn't be wasting your time reading it.</p><ul><li>you cannot filter on associations of objects inside the collection, i.e. <code>JOIN</code> (which is pretty common)</li>
<li>you cannot perform updates or deletes in bulk without loading the objects first (like set <code>published = true</code> for all articles in this collection or repository)</li>
<li>the API uses persistence words, like "where", "order by"… It's not so much a biggy but still, it's not perfect</li>
<li>you cannot chain criterias and have only 1 DB query: 1 Criteria = 1 query</li>
</ul><p>The last point is a bit vague so let me show you an example:</p><pre class="language-php">class Blog
{
    public function getPublishedArticles()
    {
        $criteria = Criteria::create()
            -&gt;where(Criteria::expr()-&gt;eq('published', true));
        return $this-&gt;articles-&gt;matching($criteria);
    }
}
$articles = blog-&gt;getPublishedArticles();
$criteria = Criteria::create()
    -&gt;setFirstResult(0)
    -&gt;setMaxResults(20);
$articlesToDisplay = $articles-&gt;matching($criteria);</pre><p>Here all the published articles will be loaded in memory, and then the first 20 results will be kept.</p><h2>The Collection interface</h2><p>Before diving in a better alternative to criterias, let's start back with an awesome Collection interface:</p><pre class="language-php">interface Collection extends Countable, IteratorAggregate, ArrayAccess
{
    function add($element);
    function get($key);
    function contains($element);
    function remove($element);
    function clear();
    function toArray();
    function count();
    function filter(Expr $predicate);
    function sort($field, $direction = 'ASC');
    function slice($offset, $length = null);
    function map(Closure $function);
}</pre><p>(let's also assume that the Repository interface also extends that interface :) )</p><h2>Filtering!</h2><p>This interface looks a lot like the Doctrine Collection interface, except that <code>filter</code> doesn't take a <code>Closure</code> anymore but an "expression", which is where the fun begin. Because it's not PHP code but an expression object (like the Criteria expression object), we can apply the same filtering in memory and in database.</p><p>Now we are get out of the scope of the interface and have a look at the implementations: <strong>what if <code>filter</code>, <code>sort</code> and <code>slice</code> where lazy?</strong></p><p>What I mean is these methods would return a new "lazy" collection that would only be loaded/computed if used. That allows some pretty nice chaining!</p><p>Example:</p><pre class="language-php">$results = $articles-&gt;filter(Expr::eq('title', 'Hello'))
        -&gt;filter(Expr::eq('author', $john))
        -&gt;sort('date', 'DESC');
// Perform a COUNT() query in database
echo count($results) . ' results';
// Fetches only 10 results from the database
$pageResults = $results-&gt;slice(0, 10);
// Executes on loaded objects, no DB query here
$allAuthors = $pageResults-&gt;map(function (Article $article) {
    return $article-&gt;getAuthor();
});</pre><p>This code would only issue 2 queries:</p><pre class="language-sql">-- Count the total number of articles
SELECT COUNT(art.id) FROM Article art
INNER JOIN User ON User.id = art.author_id
WHERE art.title = 'Hello'
ORDER BY art.date DESC
-- Fetch 10 articles for the current page
SELECT ... FROM Article art
INNER JOIN User ON User.id = art.author_id
WHERE art.title = 'Hello'
ORDER BY art.date DESC
LIMIT 0, 10</pre><h2>What about updating and deleting?</h2><p>In the same spirit, you could imagine an API to update or delete items from a collection or repository:</p><pre class="language-php">// Delete Bob's articles
$articles = $articles-&gt;filter(Expr::eq('author', $bob));
$articles-&gt;clear();
// Publish Alice's articles
$articles = $articles-&gt;filter(Expr::eq('author', $alice));
$articles-&gt;apply(Expr::set('published', true));</pre><p>Here the delete and update queries will include the previous filtering, such that there is only 1 query executed for Bob, and 1 for Alice.</p><p>Of course, that is if the objects are not loaded in memory. If they are, then the objects are updated.</p><h2>Conclusion</h2><p>This article was just an idea thrown around for discussion. There is no library available, or repository containing any code.</p><p>To sum up all this quickly:</p><ul><li>a repository is a collection (and much more too)</li>
<li>you can manipulate collections and chain filtering, sorting, etc... in a very readable way while knowing you don't trigger dozens of DB queries</li>
<li>the API shown here could be a more powerful alternative to Criterias</li>
<li>it can also provide a simpler alternative to DQL/Query Builder with the benefit of being persistence agnostic and usable in entities</li>
<li>it could allow pagination and sorting in the controllers much more easily (you wouldn't need to add these as parameters in your repositories for example)</li>
<li>it would allow for more optimized database access (counting the collection would just issue a COUNT query for example)</li>
</ul><h3>Drawbacks</h3><p>I can't finish that article without expressing what I consider as the major drawback behind all this: you are a little tiny bit persistent-aware, because you don't filter your collections with PHP code but with an arbitrary expression language.</p><p>That doesn't mean your code is coupled to the persistence layer though because it also completely works in memory, but you deliberately don't use PHP code because you <em>know</em> that it's not optimized to load all the objects from the database and then filter them with PHP.</p><p>I would say <strong>that's an acceptable compromise</strong>, because let's be honest, there is no perfect solution. <strong>You can't apply PHP code to a SQL database</strong> so you have to compromise somewhere…</p><h3>Penumbra</h3><p>Or can you? ;)</p><p>That's the bonus note on which I will finish. There is another approach (rather controversial) taken by a new project named <strong><a href="https://github.com/TimeToogo/Penumbra">Penumbra</a></strong>. Instead of playing with "expressions" to filter collections, it allows to filter using PHP code! It then parses that code to turn it into SQL code. Pretty audacious, but apparently it works (if you keep it reasonable) :)</p><p>Example:</p><pre class="language-php">$MiddleAgedUsersRequest = $UserRepository-&gt;Request()
        -&gt;Where(function (User $User) {
            return $User-&gt;GetAge() &gt; 20 &amp;&amp; $User-&gt;GetAge() &lt; 50 ;
        })
        -&gt;OrderByDescending(function (User $User) { return $User-&gt;GetLastLoginDate(); });
$SomeActiveMiddleAgedUsers = $MiddleAgedUsersRequest-&gt;AsArray();</pre><p>Will result in:</p><pre class="language-sql">SELECT Users.* FROM Users
WHERE Users.Age &gt; 20 AND Users.Age &lt; 50
ORDER BY Users.LastLoginDate DESC;</pre><p>I will not comment on whether this is good or not, because honestly I don't know. It seems like it's heavily tested and it's a serious project, so it's not just a POC or hack. I'll let you make your own mind :)</p>]]></summary>
    <link href="https://mnapoli.fr/collection-interface-and-database-abstraction/"/>
    <updated>2014-03-27T06:03:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/repository-interface/</id>
    <title><![CDATA[The Repository interface]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">10 March 2014 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Here is the definition of a repository for Eric Evans in his <a href="http://books.google.fr/books/about/Domain_driven_Design.html?id=7dlaMs0SECsC&amp;redir_esc=y">Domain Driven Design</a> book:</p><blockquote>
<p>A <em>repository</em> represents all objects of a certain type as a conceptual set (usually emulated). <strong>It acts like a collection</strong>, except with more elaborate querying capability. [...] For each type of object that needs global access, <strong>create an object that can provide the illusion of an in-memory collection</strong> of all objects of that type.</p>
</blockquote><p>While I love Doctrine, I really dislike <a href="https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/ObjectRepository.php">their repository interface</a> because it doesn't look and act like a collection. And that's too bad because Doctrine even provides a very good abstraction for collections through the <code>doctrine/collection</code> project. It even supports filtering with criterias over collections <strong>and</strong> repositories.</p><p>I know that Doctrine is not targeted at Domain Driven Design only, but I think having a better repository interface would still benefit the project and the users.</p><p>Here is a basic repository interface I tend to use instead:</p><pre class="language-php">interface Repository
{
    function add($entity);
    function remove($entity);
    function count();
    /**
     * @throws NotFoundException
     * @return object
     */
    function get($id);
    /**
     * @return array
     */
    function find(Criteria $criteria);
    function toArray();
}</pre><p>Of course, you should use this interface as a base and write your own repository interfaces for each aggregate root.</p><h2>Collection verbs</h2><p>Why use verbs like<code>add</code> and <code>remove</code> instead of <code>load</code>, <code>save</code>, <code>delete</code>, <code>persist</code>, …?</p><p>Because those are persistence-related terms and have nothing to do in an interface that is going to be used/extended in the domain layer.</p><h2>Get versus Find</h2><p>I really dislike that you can only <code>find</code> in Doctrine: it will return null if no entity is found.</p><p>Most of the time, that is not what you want. You actually don't want to "search and find" the entity, you just want to get it by its id and you assume it exists.</p><p>That's why you need to clearly differentiate between find and get:</p><ul><li>method starting with <code>get</code> should always return an entity, or throw an exception if not found</li>
<li>method starting with <code>find</code> should always return a collection (that could be empty)</li>
</ul><h2>Going further: the collection interface</h2><p>I've said it already, but a repository should behave like a collection. The interface shown above is not completely satisfying yet because it doesn't totally behave like a collection. For example you can't iterate it.</p><p>So the best solution is simple: <strong>write a sensible collection interface and have the repository extend it</strong>.</p><pre class="language-php">interface Collection extends Countable, IteratorAggregate, ArrayAccess
{
    function add($element);
    function remove($element);
    function clear();
    function contains($element);
    function get($key);
    function find(Criteria $criteria);
    function toArray();
    function slice($offset, $length = null);
}
interface Repository extends Collection
{
}</pre><p>(this is a very simple version, not exhaustive at all)</p><p>Now this is much better. <strong>You can even type-hint against the collection and accept at the same time collections and repositories!</strong></p><p>You can then iterate or filter the collection without having to care what the object really is. For example, you can write a <code>ProductCriteria</code> and use it both on the repository and collections of products.</p><p>Example: let's say you write a controller to display a product list:</p><pre class="language-php">class ProductListController
{
    /**
     * @var ProductCollection
     */
    private $products;
    public function __construct(ProductCollection $products)
    {
        $this-&gt;products = $products;
    }
    public function listAction()
    {
        return new View('product-list.twig.html', [$this-&gt;products]);
    }
}</pre><p>Here your controller is completely reusable. If you give it the <code>ProductRepository</code>, it can display the whole product list. If you give it the list of favorite products of the user, it will work too. Thank you dependency injection!</p><p>And the day PHP gets generics, that will be even nicer (<code>Collection&lt;Product&gt;</code>) but that's a debate for another day!</p>]]></summary>
    <link href="https://mnapoli.fr/repository-interface/"/>
    <updated>2014-03-10T06:03:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/working-with-mails-in-dev-environment/</id>
    <title><![CDATA[Working with mails in dev environment]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">23 September 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>If your application sends emails, you probably don't want emails to be sent when you are developing on your machine.</p><p>If you use nice libraries like <a href="http://swiftmailer.org/">SwiftMailer</a>, it is easy to use a mock instead of sending real emails. But if you don't, you can act on PHP's configuration: instead of installing and using a real SMTP server on your machine, you can fake one using a simple script.</p><p>The fake server will be a shell script: create it as <code>/usr/local/bin/sendmail-fake</code>:</p><pre class="language-sh">#!/bin/bash
{
date
echo $@
cat
} &gt;&gt; /var/log/sendmail-fake.log</pre><p>Set up file permissions and check that it works:</p><pre class="language-sh">$ sudo chmod +x /usr/local/bin/sendmail-fake
$ sudo chmod 777 /var/log/sendmail-fake.log
$ /usr/local/bin/sendmail-fake</pre><p>Now configure PHP to use it in the <code>php.ini</code>:</p><pre class="language-ini">sendmail_path = /usr/local/bin/sendmail-fake</pre><p>(and restart Apache)</p><p><strong>That's it!</strong></p><p>You can also see the emails content in <code>/var/log/sendmail-fake.log</code>.</p><p>You could even go further and edit the script to open the content of the mail into a text editor, or a browser. But that would be OS dependent, so I kept with the log file here.</p>]]></summary>
    <link href="https://mnapoli.fr/working-with-mails-in-dev-environment/"/>
    <updated>2013-09-23T04:09:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/using-metamodel-and-metaconsole-to-debug-your-application/</id>
    <title><![CDATA[Using MetaModel and MetaConsole to debug your application]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">13 September 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>I started working a few months ago on <a href="https://github.com/mnapoli/MetaModel">MetaModel</a>, a language that enables to <strong>traverse and operate your PHP model</strong>.</p><p>Today I'm going to show you how you can use MetaModel through the <a href="https://github.com/mnapoli/MetaConsole">MetaConsole</a> to debug your application.</p><p><img src="https://mnapoli.fr/images/posts/metaconsole3.gif" alt="" /></p><h2>Setup</h2><p>To integrate the MetaConsole to your project, it's very simple using Composer:</p><pre class="language-json">{
    "require-dev": {
        "mnapoli/metaconsole": "dev-master"
    }
}</pre><p>Now I create a <code>bin/meta.php</code> file (name it as you like) in my application:</p><pre class="language-php">#!/usr/bin/env php
&lt;?php
use MetaModel\Bridge\Doctrine\EntityManagerBridge;
use MetaModel\Bridge\PHPDI\PHPDIBridge;
use MetaModel\MetaModel;
// Here I set up my application (bootstrap)
require_once __DIR__ . '/../application/init.php';
$metaModel = new MetaModel();
// Integrate with the Doctrine EntityManager
$entityManager = /* ... */;
$metaModel-&gt;addObjectManager(new EntityManagerBridge($entityManager));
// Integrate with PHP-DI container
$container = /* ... */;
$metaModel-&gt;addContainer(new PHPDIBridge($container));
$console = new MetaConsole\Application('MetaConsole', null, $metaModel);
$console-&gt;run();</pre><p>As you can see, I can integrate the console with my ORM (<a href="http://www.doctrine-project.org/">Doctrine 2</a> here) and my DI container (<a href="http://php-di.org/">PHP-DI</a>). You can write bridges for other kinds of object managers or containers (and feel free to submit a Pull Request too).</p><h2>Using</h2><p>Last week, I had a bug in my application, and it was very difficult to debug because it involved data, and that was not the kind of bug you can reproduce and fix using unit tests because it involved a lot of objects. Without MetaConsole, I would have had to dig through the database using phpMyAdmin or MySQL Workbench, writing queries and joining dozens of tables.</p><p>Instead, I launched MetaConsole, and I selected an object for which I had an ID (the ID was in the URL of the webpage I wanted to debug):</p><pre>AF_Model_InputSet(562)</pre><p>This query will select the entity <code>AF_Model_InputSet</code> with ID 562, and dump it to the screen. To do this, MetaModel queries Doctrine, which roughly translate to this PHP code:</p><pre class="language-php">$entity = $entityManager-&gt;find('AF_Model_InputSet', 562);</pre><p>Now that I had my object on screen, I could <strong>traverse the object graph</strong> (through associations, getters, arrays, …) with more precise queries:</p><p><img src="https://mnapoli.fr/images/posts/metaconsole1.png" alt="" /></p><p>In the end, I ended up finding my bug: one component was marked as "required" but had a number of required fields &gt; 0 (not normal!). I fixed the code, and now I needed to rebuild some data to have everything in sync again (after my tests). That's the kind of operation that can't be done just on a single object through the website.</p><p>No problem, MetaModel let's you traverse an object graph, but also <strong>call methods on objects and on services:</strong></p><p><img src="https://mnapoli.fr/images/posts/metaconsole2.png" alt="" /></p><h2>Conclusion</h2><p>MetaModel is pretty much stable, though there are a lot of features I want to add (arguments in method calls, …).</p><p>On the other side, MetaConsole is still in development, and I hope to provide better integration with frameworks and a more enjoyable interface. If you are interested, you can try it (it's a development tool, so there's no risk since you shouldn't use it in production), and you can <a href="https://github.com/mnapoli/MetaConsole">improve it</a>.</p><p>And also, ideas are welcome!</p>]]></summary>
    <link href="https://mnapoli.fr/using-metamodel-and-metaconsole-to-debug-your-application/"/>
    <updated>2013-09-13T04:09:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/run-composer-install-when-you-switch-branch/</id>
    <title><![CDATA[Running composer install when you switch branch]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">9 September 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>When working with <a href="http://getcomposer.org/">Composer</a> and git branches, you will end up either:</p><ul><li>reinstalling dependencies each time you switch branch</li>
<li>or meeting weird bugs because you didn't</li>
</ul><p>because <code>composer.json</code> may have changed between branches.</p><p>To have composer automatically re-install dependencies when you switch to a branch, simply create a <code>.git/hooks/post-checkout</code> file in your project repository:</p><pre class="language-sh">#!/bin/sh
cd $GIT_DIR/..
composer install</pre><p>This is a git post-checkout hook (as the name suggest)</p>]]></summary>
    <link href="https://mnapoli.fr/run-composer-install-when-you-switch-branch/"/>
    <updated>2013-09-09T05:09:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/i-herd-you-like-tests/</id>
    <title><![CDATA[I herd you like tests]]></title>
    <summary><![CDATA[<article><header class="clearfix"><p class="article-info text-muted">22 August 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>So you chose to test your application using unit/functional tests.</p><p><strong>How do you ensure your tests do indeed test what you expect?</strong></p><p>Fear not! Here comes <strong>TestsTester</strong>!</p><h2>Use case</h2><pre class="language-php">&lt;?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testDate()
    {
        $entry = new Entry();
        $this-&gt;assertNotNull($entry-&gt;getDate());
    }
}</pre><p>Woops, I forgot to test that the date is in the past:</p><pre class="language-php">$this-&gt;assertLessThanOrEqual(new DateTime(), $entry-&gt;getDate());</pre><p>Now my sofware is filled with bugs!</p><h2>Usage</h2><p>With <strong>TestsTester</strong>, I would have detected that:</p><pre class="language-php">&lt;?php
class MyTestTest extends TestsTester
{
    public function testTestDate()
    {
        $test = new Test('MyTest', 'testDate');
        $test-&gt;checkAssertNotNull('$entry-&gt;getDate()');
        $test-&gt;checkAssertLessThanOrEqual('new DateTime()', '$entry-&gt;getDate()');
    }
}</pre><p>You can then run the test tests:</p><pre class="language-bash">$ testtester teststests/</pre><p>Result:</p><pre>TestsTester 1.0.0
F
There was 1 failure:
1) MyTestTest::testTestDate
'MyTest::testDate' do not test that '$entry-&gt;getDate()' is less than or equal to 'new DateTime()'.
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.</pre><p>Give it a try and check out all the crazy features at <a href="http://bit.ly/7JJSz8"><strong>TestsTester.com</strong></a>.</p><p><img src="https://mnapoli.fr/images/posts/yo-dawg.jpg" alt="" /></p></article><h2>Comments</h2>]]></summary>
    <link href="https://mnapoli.fr/i-herd-you-like-tests/"/>
    <updated>2013-08-22T04:08:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/friend-services/</id>
    <title><![CDATA[Friend services?]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">9 August 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Using the Domain Driven Design methodology, I sometimes end up on such a situation: a behavior in a model class is complex and involving other classes. It’s time to put that behavior in a service (or a factory if the situation applies to a constructor).</p><p>The problem is that in my service, I am not in the context of the domain class. If I move <code>Foo::something()</code> into <code>FooService::something($foo)</code>, then <strong>I can’t access private properties of Foo</strong>, thus limiting me to the public API of Foo.</p><p>Now I end up adding accessors, thus breaking encapsulation and complexifying everything where all I wanted was improving the code.</p><p>VB.Net has a concept of “Friend” visibility, i.e. if A is friend with B, then A can access private properties of B (or something like that it’s been a long time :p). PHP doesn’t have such a concept natively, but here is a tryout to apply it with workarounds.</p><p><em>Disclaimer</em>: the code is not pretty and is not for production. This is just an idea thrown around.</p><pre class="language-php">&lt;?php
class Foo {
    private $bar = 'hello world';
}
class FriendOfFoo {
    public function doSomething($foo) {
        return function() use ($foo) {
            echo $foo-&gt;bar;
        };
    }
}
$foo = new Foo();
$service = new FriendOfFoo();
$closure = $service-&gt;doSomething($foo)-&gt;bindTo(null, $foo);
$closure();</pre><p>See it in action on <a href="http://3v4l.org/e9RNO">3v4l.org</a>.</p><p>Here, FriendOfFoo is a service that has access to Foo’s private and protected properties.</p><p>We achieve that by writing the methods of the service into closures. We can then <a href="http://php.net/manual/en/closure.bindto.php">bind those closures to the context of Foo</a>, and voilà!</p><p>If you see a better way of achieving this, I am interested.</p>]]></summary>
    <link href="https://mnapoli.fr/friend-services/"/>
    <updated>2013-08-09T05:08:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/controllers-as-services/</id>
    <title><![CDATA[Controllers as services?]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">1 July 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>This post is sort-of a response to the <a href="http://www.whitewashing.de/2013/06/27/extending_symfony2__controller_utilities.html">blog post of Benjamin Eberlei about Controllers in Symfony 2</a>.</p><p>The subject is about Controllers and their dependencies:</p><blockquote>
<p>Controllers as a service are a heated topic in the Symfony world. Developers mainly choose to extend the base class, because its much simpler to use and less to write.</p>
</blockquote><p>With Symfony 2, you can write controllers 2 ways:</p><ol><li>
<p>extend the base Controller class. This is simpler and more practical but it ties up your controller to Symfony. Also, to fetch dependencies, you have to get them from the container, which is known as the <strong>Service Locator anti-pattern</strong> (= bad).</p>
</li>
<li>
<p>create a “normal” class, and use it as a service. That means you can use dependency injection through the constructor to get your dependencies. This is clean, this looks good, but you end up with managing a lot of dependencies :/</p>
</li>
</ol><p>To ease up solution n°2, Benjamin proposes to create a “ControllerUtility” class which would group the most used controller services. That way, you dramatically reduce the dependencies, and still hide the container.</p><p>I use a different solution.</p><h2>Constructor injection is not the only possible injection</h2><p>The idea is to keep the solution n°2, but use <strong>Property Injection</strong> instead of Constructor Injection.</p><p>Property injection is generally frowned upon, and for good reasons:</p><ul><li>injecting in a private property breaks encapsulation</li>
<li>it is not an explicit dependency: there is no contract saying your class need the property to be set to work</li>
<li>if you use annotations to mark the dependency to be injected, your class is dependent on the container</li>
</ul><p><strong>BUT</strong></p><p>if you follow best practices, <strong>your controllers will not contain business logic</strong> (only routing calls to the models and binding returned values to view).</p><p>So:</p><ul><li>you will not unit-test it (that doesn’t mean you won’t write functional tests on the interface though)</li>
<li>you may not need to reuse it elsewhere</li>
<li>if you change the framework, you may have to rewrite it (or parts of it) anyway (because most dependencies like Request, Response, etc. will have changed)</li>
</ul><p>Because of that, I chose to use Property injection.</p><h2>Property injection</h2><p>Here is what my controllers look like:</p><pre class="language-php">&lt;?php
use DI\Annotation\Inject;
class UserController
{
    /**
     * @Inject
     * @var RouterInterface
     */
    private $router;
    /**
     * @Inject
     * @var FormFactoryInterface 
     */
    private $formFactory;
    public function createForm($type, $data, $options)
    {
        // $this-&gt;formFactory-&gt;...
    }
}</pre><p>Note this is an example using <a href="http://mnapoli.github.io/PHP-DI/">PHP-DI</a>, my alternative DI Container. It allows to mark injections using annotations.</p><p>I know many PHP devs don’t like annotations, and there are some reasons not to use it. But in this case, because of the points I explained above, I find it acceptable to use the <code>@Inject</code> annotation. I find it also extremely practical.</p><p>Of course, this example also applies without using annotations (using a configuration file f.e.), and <strong>it also applies to Symfony’s container</strong>.</p><p>In the end:</p><ul><li>controllers don’t use the container</li>
<li>controllers can be reused elsewhere, given they are fetched through the container</li>
<li>we have full auto-completion and refactoring support in IDEs</li>
<li><strong>controllers are easy and fast to write and read</strong> (and that’s something I value a lot)</li>
</ul><p>By the way, some Java developers may find this pattern of code familiar, it’s inspired from when I was working with Spring :)</p><h2>Performance note</h2><blockquote>
<p>You are injecting services that may not be used</p>
</blockquote><p><a href="http://php-di.org/">PHP-DI</a> and <a href="http://symfony.com/doc/current/components/dependency_injection/index.html">Symfony DIC</a> both support lazy injection, i.e. injecting a proxy that will load the target service only when it is used.</p>]]></summary>
    <link href="https://mnapoli.fr/controllers-as-services/"/>
    <updated>2013-07-01T08:07:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/should-you-really-write-ugly-code-no/</id>
    <title><![CDATA[Should you really write ugly code? Spoiler: no]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">9 June 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>I recently stumbled upon François Zaninotto’s blog post: <a href="http://redotheweb.com/2013/06/04/you-should-write-ugly-code.html">You should write ugly code</a>. While he makes several good points, I strongly disagree with him and I feel the urge to give my opinion on the matter (what did you expect, this is a blog).</p><p>The point that he makes is that he encourages developers to write “ugly code”, i.e. code that works and that doesn’t follow best practices or anything related to code quality.</p><blockquote>
<p>[..] developers shouldn’t care about code beauty, because that’s not their job. Instead, they should focus on creating great products, which is infinitely more satisfying.</p>
</blockquote><p>Later, he insists that by writing ugly code, we ship faster than if we had to worry and handle code quality. And shipping faster is good for the business.</p><p>Well that’s true. That’s called <strong>Technical Debt</strong>. And like any debt, you’ll have to repay later.</p><p><img src="https://mnapoli.fr/images/posts/technical-debt.png" alt="" /></p><p><em>Technical debt diagram, borrowed from Planet Geek for the (awesome) “<a href="http://www.planetgeek.ch/2013/06/05/clean-code-cheat-sheet/">Clean Code cheat sheet</a>“.</em></p><p>Payback is a bitch and future-you will be cursing present-you for writing ugly code. The cost of change is directly affected, and when you tell your boss that Feature B will takes 2 weeks, he will point out that Feature A took only 2 days.</p><p>I won’t get radical as well and say that you should alway write “beautiful code”. What I advocate for is to carefully choose your side.</p><p>You need to get a prototype working for a demo? Then get it done quick and dirty! Heck I even used <a href="http://php.net/manual/fr/function.mysql-query.php">mysql_query</a> on an internal website once, and it worked. I know that this website will never need any real maintenance, so I can sleep without fearing for <a href="http://www.codinghorror.com/blog/2008/06/coding-for-violent-psychopaths.html">violent psychopaths</a>.</p><p>But if you plan on writing software that will be maintained for a few years, you would definitely earn by writing beautiful code. Code quality earns you money, and that’s something difficult for managers to get (especially if they have never developed, or never seen code quality benefits before). I once took part on the rewrite of a whole application. After the rewrite, the manager was completely amazed that we needed only a few days to do what would systematically take a month or two before.</p><p>I’d like to finish on his take about “<em>coding trends</em>” and what he calls “<em>trend setters</em>” like Eric Evans with his <a href="http://en.wikipedia.org/wiki/Domain-driven_design">Domain Driven Design</a>. DDD is not a trend, it is a set of patterns and methods to help you in specific situations. <a href="http://www.amazon.com/dp/0321125215/ref=cm_sw_su_dp">Evans book</a> is very clear about it: DDD can be unhelpful if you try to use it everywhere. That is very reductive to say that all these kind of people (Evans, Martin Fowler, Jeff Atwood) do is “<em>to explain why pattern A is better than pattern B. Until someone else publishes a book, explaining that pattern C is much, much better.</em>”</p><p>Thanks to these guys, and best practices in general, I make my company earn money with apps that don’t need to be rewritten every year. I can work on my colleague’s code without pulling my hair and wishing him a painful death. I can participate to Open Source projects because I can understand the code.</p><p><strong>TL;DR</strong></p><ul><li>Go quick and dirty, but expect troubles on the long run</li>
<li>Care about quality, that will slow you down but get you farther</li>
</ul><p>Both are valid options, but choose carefully.</p>]]></summary>
    <link href="https://mnapoli.fr/should-you-really-write-ugly-code-no/"/>
    <updated>2013-06-09T08:06:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/code-coverage-reports-with-github-travis-and/</id>
    <title><![CDATA[Code coverage reports with GitHub, Travis and Coveralls]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">8 June 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>You have a PHP project hosted on GitHub with continuous integration using Travis-CI?</p><p>How about setting up <strong>code coverage reports</strong>?</p><p>For example, here is the code coverage report of <a href="http://mnapoli.github.io/PHP-DI/">PHP-DI</a>: <a href="https://coveralls.io/r/mnapoli/PHP-DI?branch=master"><img src="https://coveralls.io/repos/mnapoli/PHP-DI/badge.png?branch=master" alt="Coverage Status" /></a> (click on the link to see the details).</p><p>To do so, you will need to create an account and enable your project at <a href="https://coveralls.io/">Coveralls</a>. Then add this to your <code>composer.json</code>:</p><pre class="language-json">"require-dev": {
    "satooshi/php-coveralls": "dev-master"
}</pre><p>Finally, update your <code>.travis.yml</code> configuration:</p><pre class="language-yaml">language: php
php:
 - 5.3
 - 5.4
 - 5.5
before_script:
 - wget http://getcomposer.org/composer.phar
 - php composer.phar install --dev --no-interaction
script:
 - mkdir -p build/logs
 - phpunit --coverage-clover build/logs/clover.xml
after_script:
 - php vendor/bin/coveralls -v</pre><p>Now if you commit and push, Travis will run the tests and push the code coverage results to Coverall. Check out your project page on Coverall!</p>]]></summary>
    <link href="https://mnapoli.fr/code-coverage-reports-with-github-travis-and/"/>
    <updated>2013-06-08T08:06:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/overriding-dependencies-with-composer/</id>
    <title><![CDATA[Overriding dependencies with Composer]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">16 April 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>At my company, <strong>My C-Sense</strong>, we use Doctrine amongst other PHP frameworks and libraries. When we find bugs (or need new features), we contribute to the project through our <a href="https://github.com/myclabs">open source initiative</a>.</p><p>The problem is when we submit a pull request on Github, several months usually happen until our fix appears in a stable release.</p><p>To be able to enjoy our bugfixes immediately, here is our workflow:</p><ul><li>We fork the repository of the project to <a href="https://github.com/myclabs">our organization account</a></li>
<li>We commit and publish the bugfix in a branch of our repository</li>
<li>We submit a Pull Request</li>
<li>We override the dependency to the project with our version in Composer</li>
</ul><p>Overriding a dependency <a href="http://getcomposer.org/doc/04-schema.md#repositories">is quite simple</a>: just add your git repository in your <code>composer.json</code> and require you branch.</p><p>But when we want to override, for example, <code>doctrine/common</code> which is used by <code>doctrine/orm</code>, then we have a problem: <code>doctrine/orm</code> wants a stable version of <code>doctrine/common</code>, it will conflict with your requirement to a dev branch.</p><p>The solution is to <strong>alias your dev branch to a stable release</strong>, and that is possible through the awesome “<a href="http://getcomposer.org/doc/articles/aliases.md#require-inline-alias">inline alias</a>” functionality in Composer.</p><p>Here is an example:</p><pre class="language-json">{
    "require": {
        "doctrine/orm": "2.3.*",
        "doctrine/common": "dev-ChainDriverFix as 2.3.0"
    },
    "repositories": [
        {
            "type": "git",
            "url": "https://github.com/myclabs/common.git"
        }
    ]
}</pre><p>Here, our branch <code>ChainDriverFix</code> will override the 2.3.0 version of <code>doctrine/common</code>, which will also be compatible with <code>doctrine/orm</code>!</p>]]></summary>
    <link href="https://mnapoli.fr/overriding-dependencies-with-composer/"/>
    <updated>2013-04-16T08:04:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/the-optional-singleton-pattern/</id>
    <title><![CDATA[The “Optional Singleton” pattern]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">22 March 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>The <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton</a> is a practical design pattern, that’s the reason it is so popular amongst beginners. It is also an anti-pattern because of the problems it introduces (global state, difficult to test, …).</p><p>While I agree with that, and the fact that Singletons should be used with (a lot of) moderation, I also like an alternative pattern which comes with the advantage of the singleton and balances out its disadvantages. This can be useful if you have to work on a codebase that has singletons.</p><p>I’m calling this pattern the <strong>Optional Singleton</strong> for lack of a better name.</p><p>Simply put, this is a class which you can use as a singleton, or not (it’s optional ;):</p><ul><li>you can still use the handy <code>MySingleton::getInstance()</code></li>
<li>you can however create new instances of the class, for example for tests</li>
</ul><p>There is nothing revolutionary about it, see for yourself:</p><p>Of course, this is a pattern that has to be used where it makes sense. Singletons, as cool as they can be, will never do better than dependency injection.</p><hr /><p><strong>Update</strong>: I’ve received numerous responses (mostly “the singleton is an anti-pattern” which I agree to). Here is one of my response that I’d like to have here as well:</p><blockquote>
<p>The entire point of the singleton pattern is that you <strong>can’t</strong> instantiate the class. That’s why the pattern is called singleton.</p>
</blockquote><p>My answer:</p><blockquote>
<p>Yes, but in 90% of its derived usage it’s not because we want only one instance, it’s because it’s practical.</p>
<p>Quote from wikipedia: “There is criticism of the use of the singleton pattern, as some consider it an anti-pattern, judging that it is overused, <strong>introduces unnecessary restrictions in situations where a sole instance of a class is not actually required</strong>, and introduces global state into an application.”</p>
<p>For example one may use the singleton pattern for services: accessing them is practical, you can access them anywhere with the singleton pattern. I’ve seen codebases with this pattern.</p>
<p>Now if I come on a codebase using the singleton for services, and if I can’t rewrite everything, I’ll turn the Singletons into “Optional Singletons” so that the existing code still work, and so that I can use Dependency Injection over those services in the new code that I’ll write.</p>
</blockquote>]]></summary>
    <link href="https://mnapoli.fr/the-optional-singleton-pattern/"/>
    <updated>2013-03-22T08:03:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/confirm-dialogs-for-html-links-and-buttons/</id>
    <title><![CDATA[jQuery plugin: Confirm dialogs for HTML links and buttons]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">5 March 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>This is the first open source project created at my company, so I am quite proud of it even though it is not much.</p><p>The name is <a href="http://myclabs.github.io/jquery.confirm/"><strong>jquery.confirm</strong></a>, which is pretty explicit. It lets you have confirmation dialogs for links or buttons. For example, <strong>"Are you sure you want to delete that comment?"</strong> kind of things.</p><p>I am going to present its basic usage and options here.</p><p>The idea is to write <a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript">unobtrusive Javascript</a> by letting the user write clean HTML:</p><pre class="language-html">&lt;a href="home" class="confirm"&gt;Go to home&lt;/a&gt;</pre><p>To enable confirmation on this link, simply:</p><pre class="language-javascript">$(".confirm").confirm();</pre><p>That will show a confirmation dialog each time the user clicks the link. If the user confirms, the plugin will then redirect him to the link.</p><p>You can configure the texts and labels through the options. You can also change the actions that are executed when the user confirms (follow the link) or cancels (do nothing), so you can perform AJAX requests for example.</p><p>One interesting option is to force the link to be called with a POST request instead of a GET:</p><pre class="language-javascript">$(".confirm").confirm({
    post: true
});</pre><p>On your server-side code, you can check that the request is POST and refuse GET request. That can help prevent security issues like someone sending a link to delete someone else’s account for example: <a href="http://example.com/my-account/delete">http://example.com/my-account/delete</a>. If you only accept POST request, people clicking on that link won’t see their account deleted (because the request would be a GET).</p><p>If you want to learn more or try it, the website contains the <a href="http://myclabs.github.io/jquery.confirm/">official documentation and some demos</a>.</p>]]></summary>
    <link href="https://mnapoli.fr/confirm-dialogs-for-html-links-and-buttons/"/>
    <updated>2013-03-05T08:03:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/be-a-better-programmer-take-a-step-back/</id>
    <title><![CDATA[Be a better programmer: take a step back]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">4 February 2013 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p><em>Replace [client] by [boss] or anything of the kind if you prefer.</em></p><h2>A day at work</h2><blockquote>
<p><strong>Bug #3890 from Client</strong></p>
<p>There is an application crash, it says “division by zero error in SpeedCalculator::compute()”.</p>
<p>Please fix ASAP!</p>
</blockquote><p>You open <code>SpeedCalculator.php</code> to find:</p><pre class="language-php">public function compute() {
    return $this-&gt;distance / $this-&gt;time;
}</pre><h2>Fixing the bug</h2><p>Easy! Who wrote that code anyway, how could he not think of that!</p><pre class="language-php">public function compute() {
    if ($this-&gt;time == 0) {
        return 0;
    }
    return $this-&gt;distance / $this-&gt;time;
}</pre><p>There you go, the bug is fixed in 2 minutes.</p><p>Later, the same bug is found in <code>RatioCalculator</code> and <code>MoneyCalculator</code>, but once these are fixed too, everyone in the team is sure the problem won’t appear anywhere, it’s gone, for sure this time! The code is rock solid now!</p><p>A month later, another bug pops in. The application does not crash anymore, but the client happens to see wrong calculation results in his reports because of the <code>return 0;</code>.</p><h2>Take a step back</h2><p>What if, instead of rushing, we took a step back.</p><blockquote>
<p>Why did this situation happen?</p>
<p><code>$this-&gt;time</code> was set to 0.</p>
</blockquote><p>Easy! Let’s prevent that.</p><pre class="language-php">public function setTime($time) {
    if ($time == 0) {
        throw new InvalidArgumentException("Invalid value");
    }
    $this-&gt;time = $time;
}</pre><p>Now this is better, you guarantee the integrity of the data. But the client is not very happy! When he fills the form with a time of 0, the application shows an error page.</p><p>So you work on displaying a nice error message by catching the exception in the controller.</p><p>When you’re done, you realize you also got the same thing to do for RatioCalculator and MoneyCalculator, so you copy paste and you are good to go.</p><p>Wait a minute, the client prefers that the error message is displayed in orange rather than red. So you change the color and copy-paste the code again.</p><h2>Take another step back</h2><p><strong>What if, instead of fixing a bug, you answered a need?</strong></p><p>Why did the client put 0 in the form? Because he made a mistake.</p><p>What is needed here?</p><ul><li>Is it <strong>only</strong> making sure the time that the user inputs in “speedCalculationForm” is ≠ 0?</li>
<li>Is it <strong>only</strong> making sure the “speedCalculationForm” contains valid data?</li>
<li><strong>Or is it validating all user inputs?</strong></li>
</ul><p>So what about a validation library for example?</p><p>Waaaaait! Don’t go and write one yourself! For the love of god, take a step back, breathe, and look at what already exists.</p><h2>Needs</h2><p>We, programmers, love being technical. When your client or your boss thinks out loud about what he wants, we can’t help but imagine how we could implement it.</p><p>But we need be able to take a step back. If we want to be really good in our jobs, <strong>we have to understand the needs before thinking about solutions</strong>. And that takes a lot of effort.</p><p>Does the client really need “a blinking button that moves away when you try to click on it?” or does he need something else, something that he doesn’t know about and that you could help him define? And the same goes for yourself! Do you really need to open a file and write some infos in there, or do you simply need a logging system?</p><p>Take a step back, try and see the big picture. Because one may be a very good programmer, but code’s purpose is to answer a need.</p><p>Not a <em>"fix the bugs in the bug tracker"</em> kind of need, but rather a <em>"I want an application will help me calculate speed based on input data, and if I type in invalid data then for fuck’s sake just tell me don’t go and calculate some weird results"</em>.</p>]]></summary>
    <link href="https://mnapoli.fr/be-a-better-programmer-take-a-step-back/"/>
    <updated>2013-02-04T08:02:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/doctrine-schema-validation-in-a-phpunit-test/</id>
    <title><![CDATA[Doctrine schema validation in a PHPUnit test]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">10 December 2012 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Doctrine offers a <a href="http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html#runtime-vs-development-mapping-validation">command line option to validate the schema</a> (or mapping):</p><pre>./doctrine orm:validate-schema</pre><p>This is very useful, when I ran it against my code, which was <em>working</em> by the way, I got several errors/warnings.</p><p>However, I didn’t want to have to run this tool manually once in a while. I already have tests for that. So I thought: <strong>why not integrating the schema validation to the tests!</strong></p><p>So here is my implementation of a PHPUnit test failing when Doctrine validation find errors:</p>]]></summary>
    <link href="https://mnapoli.fr/doctrine-schema-validation-in-a-phpunit-test/"/>
    <updated>2012-12-10T08:12:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/introduction-dependency-injection-with-real-life-example/</id>
    <title><![CDATA[Introduction to Dependency Injection with a real life example]]></title>
    <summary><![CDATA[<article><header class="clearfix"><p class="article-info text-muted">6 December 2012 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>This example is an introduction to the <strong>Dependency Injection</strong> concept. It is based on the PHP library <a href="http://mnapoli.github.com/PHP-DI/">PHP-DI</a>.</p><h2>Classic implementation</h2><p>Given you have:</p><pre class="language-php">class GoogleMapsService {
    public function getCoordinatesFromAddress($address) {
        // calls Google Maps webservice
    }
}
class OpenStreetMapService {
    public function getCoordinatesFromAddress($address) {
        // calls OpenStreetMap webservice
    }
}</pre><p>The classic way of doing things is:</p><pre class="language-php">class StoreService {
    public function getStoreCoordinates($store) {
        $geolocationService = new GoogleMapsService();
        // or $geolocationService = GoogleMapsService::getInstance() if you use singletons
        return $geolocationService-&gt;getCoordinatesFromAddress($store-&gt;getAddress());
    }
}</pre><p>Now we want to use the OpenStreetMapService instead of GoogleMapsService, how do we do? We have to change the code of StoreService, and all the other classes that use GoogleMapsService.</p><p><strong>Without dependency injection, your classes are tightly coupled with their dependencies.</strong></p><h2>Dependency injection implementation</h2><p>The StoreService now uses dependency injection:</p><pre class="language-php">class StoreService {
    /**
     * @Inject
     * @var GeolocationService
     */
    private $geolocationService;
    public function getStoreCoordinates($store) {
        return $this-&gt;geolocationService-&gt;getCoordinatesFromAddress($store-&gt;getAddress());
    }
}</pre><p>And the services are defined using an interface:</p><pre class="language-php">interface GeolocationService {
    public function getCoordinatesFromAddress($address);
}
class GoogleMapsService implements GeolocationService {
    public function getCoordinatesFromAddress($address) {
        // calls Google Maps webservice
    }
}
class OpenStreetMapService implements GeolocationService {
    public function getCoordinatesFromAddress($address) {
        // calls OpenStreetMap webservice
    }
}</pre><p>If you use <a href="http://mnapoli.github.com/PHP-DI/">PHP-DI</a> (a PHP dependency injection library), you then configure which implementation will be used:</p><pre class="language-php">$container-&gt;set('GeolocationService')
          -&gt;bindTo('OpenStreetMapService');</pre><p>If you change your mind, there’s just one line of configuration to change.</p></article><h2>Comments</h2>]]></summary>
    <link href="https://mnapoli.fr/introduction-dependency-injection-with-real-life-example/"/>
    <updated>2012-12-06T08:12:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/php-fig-should-define-php-interfaces/</id>
    <title><![CDATA[The PHP-FIG should define PHP interfaces]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">23 November 2012 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>Bouncing on the discussion initiated in the <a href="https://github.com/php-fig/fig-standards/issues/57">#52</a> ticket of the PHP-FIG project on Github: « <strong>Explain the scope of the PSR system</strong> », I’ll explain the case I’m trying to make.</p><p>First, <a href="http://www.php-fig.org/"><strong>PHP-FIG</strong></a>, which stands for <em>Framework Interoperability Group</em>, is a gathering of major PHP frameworks and project who try to:</p><blockquote>
<p>talk about the commonalities between our projects and find ways we can work together.</p>
</blockquote><p>This group has released PSR-0, PSR-1 and PSR-2, three specifications of coding standards, guide style and code organisation (for autoloading interoperability). Now the question is asked: is it the role of the PHP-FIG to define technical “code” specifications or is it out of its scope? Here is my answer.</p><p><strong>PSR-0/1-2 are contracts between its users to ensure cohesiveness and compatibility.</strong></p><p>Think of the PSR-0 for example, it enabled all projects to be compatible regarding class autoloading. To achieve this, no code or PHP interface was necessary because what the autoloading needed was only file names, directories and class names constraints.</p><p>Now there are other questions that need standardization for interoperability between PHP projects. And some of them <strong>need</strong> PHP interfaces.</p><p>For example, PHP (or the SPL) <a href="https://github.com/php-fig/fig-standards/issues/59">does not define a Collection interface</a> (or any implementation). However, a Collection is a base object, and I bet it is used (or could be used) in many projects. Now Doctrine defined their own Collection interface (because it needed it) and I’m sure other projects did the same for the same reasons, but that situation is stupid. A Collection is a standard data structure, implementations may vary but the Collection interface should be defined once and for all.</p><p>And <strong>PHP interfaces are contracts between its users to ensure cohesiveness and compatibility</strong>.</p><p>Notice any similarity between PSR-0/1/2 and interfaces? They are the same thing, applied to different things. They are technical specifications.</p><p>I agree that the SPL was a good start and maybe would have been a good place for such things, but it is a still project, with no big changes lately, a lot of inertia and several big lacks (and who decides what’s in the SPL?). The PHP FIG is the perfect group to bring a solution to this: it is active, dynamic, open and transparent, representative of the major PHP projects, and it has the competences and the momentum to make it useful and used (that will not be “yet another PHP library”, it will be used by major frameworks).</p><p>If PHP-FIG doesn’t do it, then who will (and more importantly: who will make it a success)?</p><p>And to extend my point, have a look on the Java side (JSR), and for example <a href="http://jcp.org/aboutJava/communityprocess/jsr/cacheFS.pdf">JSR-107</a> which defines interfaces for cache API, or <a href="http://en.wikipedia.org/wiki/Java_Persistence_API">JSR-220</a> which defines JPA (specification of persistence API that Doctrine 2 has followed).</p><p><strong>TL/DR</strong>: I think <strong>PHP-FIG should define and provide PHP interfaces</strong>. PHP-FIG defines technical specifications for interoperability between PHP projects. PHP interfaces are a form of technical specifications, they can allow PHP projects to be more compatible and work better together. PHP-FIG is the best group possible to standardize classic/mainstream API (utility classes, …). Java does it, it works, that should inspire us.</p>]]></summary>
    <link href="https://mnapoli.fr/php-fig-should-define-php-interfaces/"/>
    <updated>2012-11-23T08:11:00+01:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/doctrine-2-yaml-reference/</id>
    <title><![CDATA[Doctrine 2 YAML reference]]></title>
    <summary><![CDATA[<header class="clearfix">
<p class="article-info text-muted">5 October 2012 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>If you are working with Doctrine 2 and its YAML configuration files for the object mapping, you may find the documentation lacking some details.</p><p>Here is a gist of all the YAML syntax possible:</p><p>It is based on <a href="http://blog.hio.fr/2011/09/17/doctrine2-yaml-mapping-example.html">this blog article</a>, completed, and turned into a git repository (thanks to github), so anyone can fork it and improve it.</p>]]></summary>
    <link href="https://mnapoli.fr/doctrine-2-yaml-reference/"/>
    <updated>2012-10-05T08:10:00+02:00</updated>
  </entry>
  <entry>
    <id>https://mnapoli.fr/dependency-injection-with-php/</id>
    <title><![CDATA[Dependency Injection with PHP]]></title>
    <summary><![CDATA[<header class="clearfix"><p class="article-info text-muted">20 September 2012 - <a href="https://mnapoli.fr/">Matthieu Napoli</a></p>
</header><p>I used to develop using Singletons, registries or even static classes. Those days are gone.</p><p>I decided to use <strong>Dependency Injection</strong> so that:</p><ul><li>my classes would be testable</li>
<li>replacing an implementation by another would be not only doable, but easy (and so extending a library/module would too)</li>
<li>the design of those classes wouldn’t be guided by the question of “how they will be used”</li>
<li>my code would be cleaner, simpler</li>
<li>and IDE auto-completion/type-hinting would always work</li>
</ul><p>I gave a try to Symfony and ZF2 DI systems, but they both seem way too complicated for just a simple need (that anyone who has worked with Spring would understand):</p><pre class="language-php">class MyClass {
    /**
     * @Inject
     * @var MyService
     */
    private $service;
}</pre><p>This short code means: <em>Inject, using a simple annotation, an instance of another class into a property</em>.</p><p>I started working on a framework enabling such functionality few months ago. It is now in a mature state. It is based on the <a href="http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html">Annotations library of Doctrine 2</a>, and takes most of its ideas of Spring 3.</p><p>You can check out this framework on its official website: <a href="http://mnapoli.github.com/PHP-DI/">PHP-DI</a>, and you are welcome to use it or contribute.</p>]]></summary>
    <link href="https://mnapoli.fr/dependency-injection-with-php/"/>
    <updated>2012-09-20T08:09:00+02:00</updated>
  </entry>
</feed>
