[{"data":1,"prerenderedAt":224},["ShallowReactive",2],{"navigation":3,"page-\u002Fopen-source":16},[4,8,12],{"title":5,"path":6,"stem":7},"About","\u002F","1.index",{"title":9,"path":10,"stem":11},"Work","\u002Fwork","2.work",{"title":13,"path":14,"stem":15},"Open Source","\u002Fopen-source","3.open-source",{"id":17,"title":13,"body":18,"description":218,"extension":219,"meta":220,"navigation":221,"path":14,"seo":222,"stem":15,"__hash__":223},"pages\u002F3.open-source.md",{"type":19,"value":20,"toc":207},"minimark",[21,25,29,32,35,40,51,54,57,60,64,67,70,73,77,80,83,87,90,93,96,100,103,132,135,139,142,187,194,198],[22,23,13],"h1",{"id":24},"open-source",[26,27,28],"p",{},"I've been contributing to open source since 2010. Much of it has been maintenance: taking over packages other people started, and keeping them working.",[26,30,31],{},"Writing a library is the easy part. Maintaining one means triaging issues, cutting releases, and occasionally making a change that breaks somebody's build for a reason you have to be able to defend in public.",[26,33,34],{},"Figures below are as of August 26, 2026.",[36,37,39],"h2",{"id":38},"pagerfanta","Pagerfanta",[26,41,42,43,50],{},"I took over ",[44,45,39],"a",{"href":46,"rel":47},"https:\u002F\u002Fgithub.com\u002FBabDev\u002FPagerfanta",[48,49],"noopener","noreferrer"," in June 2020. It's a pagination library for PHP that had been around since 2011 and had gone quiet.",[26,52,53],{},"The first thing I did was take it apart. It shipped as a single package with adapters for Doctrine ORM, Doctrine DBAL, MongoDB ODM, PHPCR, Elastica, and Solarium built in — so installing it to paginate an array in memory dragged in dependency constraints for six data sources you weren't using. Since 3.0 it's ten focused packages: a core, a Twig integration, and one adapter each. You install what you actually page over.",[26,55,56],{},"289 packages depend on Pagerfanta, and it's installed around 640,000 times a month. The core package — the split I designed — has been installed 9.3 million times since 2020. I maintain the Symfony integration alongside it, at 20.2 million installs and 73 dependents.",[26,58,59],{},"The original package carries 53 million lifetime installs going back to 2011, most of them from before I took it on.",[36,61,63],{"id":62},"websockets","WebSockets",[26,65,66],{},"I sent 22 patches to an existing Symfony WebSocket bundle before writing my own. When that project stopped moving, I wrote a WebSocket server library for PHP, with the Symfony integration as a separate bundle on top.",[26,68,69],{},"That split is the point. The bundle I'd been patching pulled in a full WebSocket stack, two more bundles, and around ten Symfony components — so running a WebSocket server meant building a Symfony application. Mine depends on the RFC 6455 protocol implementation, ReactPHP's event loop and sockets, and the two Symfony components it actually uses. The server runs anywhere; the bundle is optional.",[26,71,72],{},"Both are small: a little over a thousand installs a month each.",[36,74,76],{"id":75},"transifex","Transifex",[26,78,79],{},"I wrote a PHP client for the Transifex API to get better translation automation on a Joomla project, then used it to build Mautic's translation infrastructure, which rebuilds its language packs from Transifex every day.",[26,81,82],{},"I stopped maintaining it at around 157,000 installs. In January 2020 the Mautic team forked it in-house to keep their pipeline running.",[36,84,86],{"id":85},"sylius-plugins","Sylius plugins",[26,88,89],{},"Two of my Sylius plugins came out of a client build: a shipping estimator and product samples. Each solved a real problem for one store, and neither was specific to that store.",[26,91,92],{},"If a feature isn't about the client paying for it, it has no business being trapped in their codebase. They get the feature, the ecosystem gets a package, and I get one implementation to maintain instead of two.",[26,94,95],{},"Adoption on both is minimal.",[36,97,99],{"id":98},"joomla","Joomla",[26,101,102],{},"I spent about a decade in the Joomla project.",[104,105,106,114,120,126],"ul",{},[107,108,109,113],"li",{},[110,111,112],"strong",{},"Release coordinator for four feature releases"," — 3.2, 3.3, 3.8, and 3.9. Joomla 3.9 was the project's privacy release; the GDPR compliance tooling shipped under it.",[107,115,116,119],{},[110,117,118],{},"Core developer on the Joomla Framework",", through its 1.x and 2.0 releases.",[107,121,122,125],{},[110,123,124],{},"Security team"," member for years, and its lead for part of them.",[107,127,128,131],{},[110,129,130],{},"Board of Directors of Open Source Matters, Inc.",", the not-for-profit behind the project, as Production Department Coordinator.",[26,133,134],{},"I also built and maintained Joomla's official Docker image and shipped its releases through Docker's official images program. It's still an official image, across eight processor architectures; other people maintain it now.",[36,136,138],{"id":137},"elsewhere","Elsewhere",[26,140,141],{},"Around 535 merged pull requests into other people's projects. The ones I've put the most into:",[104,143,144,150,156,162,168],{},[107,145,146,149],{},[110,147,148],{},"Doctrine Extensions"," — 78 merged",[107,151,152,155],{},[110,153,154],{},"JWT Refresh Token Bundle"," — 43 merged. Refresh-token rotation under real concurrency came up again years later in my day job; that bundle is where I first worked through the problem.",[107,157,158,161],{},[110,159,160],{},"Sylius"," — 31 merged across the core and its resource bundle",[107,163,164,167],{},[110,165,166],{},"Symfony"," — 15 merged into the framework itself",[107,169,170,173,174,173,177,173,180,183,184],{},[110,171,172],{},"JMS Serializer",", ",[110,175,176],{},"Liip's Imagine and test-fixtures bundles",[110,178,179],{},"Doctrine Bundle",[110,181,182],{},"FOSRestBundle",", and ",[110,185,186],{},"Packagist",[26,188,189,190,193],{},"I was also one of the lead developers on ",[110,191,192],{},"Mautic"," through its 1.0 release, and I wrote its original Docker image, which is still active eleven years later at 481 stars and 345 forks.",[36,195,197],{"id":196},"documentation-lives-elsewhere","Documentation lives elsewhere",[26,199,200,201,206],{},"The packages have their own home. Installation, configuration, upgrade guides, and release notes are at ",[44,202,205],{"href":203,"rel":204},"https:\u002F\u002Fwww.babdev.com",[48,49],"babdev.com",".",{"title":208,"searchDepth":209,"depth":209,"links":210},"",2,[211,212,213,214,215,216,217],{"id":38,"depth":209,"text":39},{"id":62,"depth":209,"text":63},{"id":75,"depth":209,"text":76},{"id":85,"depth":209,"text":86},{"id":98,"depth":209,"text":99},{"id":137,"depth":209,"text":138},{"id":196,"depth":209,"text":197},"The packages I maintain, the projects I contribute to, and a decade in Joomla — four feature releases, the security team, and its framework.","md",{},true,{"title":13,"description":218},"IwBzwpw3AQU02BtstwDAgjFSuQtxnEAb0AQQYC2e0I4",1787768014282]