Join InterNACHI’s fully remote dev team

InterNACHI® is a non-profit trade association and internationally-accredited school that helps educate, support, and promote small businesses around the world.

Our mission is made possible through a strong focus on technology. We built and maintain in-house systems for:

  • Online education and testing
  • Licensing and certification management
  • Back office functions like scheduling and pricing
  • Billing and payouts
  • Electronic agreements and e-signatures
  • And dozens more

Our stack at a glance

While we generally try to balance “what we know well” with “what’s right for the job,” we rely heavily on a handful of technologies:

  • PHP
  • Laravel
  • MySQL
  • Tailwind
  • Alpine.js
  • React
  • macOS
  • Linux
  • Node.js
  • nginx
  • Ansible
  • AWS

Join our small team of exceptional developers

Photo of Chris Morrell

Chris Morrell

Team member since 1997
UTC-4
InterNACHI’s CEO & CTO, with over 25 years of dev experience. Father of two, board game nerd, and avid open-source contributor (including Laravel core).
he/him
Photo of Bogdan Kharchenko

Bogdan Kharchenko

Team member since 2018
UTC-4
Father-in-training, enjoy motorcycle racing. Been hooked on Laravel for 8+ years & have a handful of PRs merged with my name on them into various open source projects.
he/him
Photo of Skyler Katz

Skyler Katz

Team member since 2023
UTC-5
PHP Developer since 2005. Father of one, improv theater nerd, and Saint Louis City SC fan.
he/him

Get to know us

Who you work with matters. Check out some of our team members’ conference talks, podcast appearances, and other videos to get behind-the-scenes looks at who we are and how we build software.

It's not just the people, though…

Remote-First Team

Competitive Salaries

100% Health Coverage

100% Dental Coverage

100% Vision Coverage

401(k) Matching

Home Office Stipend

Now about you…

Contribute across our entire stack. Every developer at InterNACHI® is full-stack—each bringing their own distinct strengths. We're looking for someone who can both keep up and bring their own experience to the mix, filling gaps where we need them most (more on that below).

The Essential Foundation

Everyone on the dev team writes and ships code daily. You should be familiar with the core components of our stack (PHP/JS/MySQL/etc) and bring some additional expertise to the table.

  • You care about great code
  • You’ve got years of professional Laravel experience
  • You understand RDBMs, and can model and query data efficiently
  • You have solid experience contributing to a git repo
  • You’ve established some expertise in leveraging LLMs for software development
  • You enjoy pair-programming with other opinionated devs
  • Security, accessibility, and performance are priorities for you, not afterthoughts

Got the foundation covered? Then let's talk about how you might best stand out from the crowd. There are lots of ways you can help our team. Here are a few…

The Mysterious Back End

Write and ship Laravel code. There are features to build, and you’re the one to build them. Data modeling and migrations. Security and performance. Simple readable code.

The Mercurial Front End

Spend your time in Blade, Tailwind, and React. You've got a good eye for design, and you’re not afraid of HTML or JavaScript when you come across it.

The Mythical DevOps

Optimization, provisioning, scaling, maintaining. We deploy code many times every day, and you want to constantly make that better.

The Methodical Product Focus

Get to the bottom of what matters. Focus on process, underlying problems, and real solutions. You're in Linear tickets, focused on problem statements and customer interviews.

Bring something else to the table? We want to hear about it. We know some of what we need, but also just want to work with great devs who can bring value. There are a lot of ways to contribute.

A little more about what to expect:

Your typical week
Expect to spend about 80% of your time on your work, and 20% of your time meeting with the team and the rest of the organization. You’ll participate in daily standups and bi-weekly retrospectives and planning.
Your tools
We’ll send you a Macbook and get you set up on GitHub, Tuple, Linear, Slack, Claude, and Zoom. We’ll also set up PhpStorm and Laravel IDEA for development, and help you get our app running locally via Laravel Herd.
Hours
Your typical workday should overlap with UTC-4 (eastern time) for at least six hours each day.
Collaboration
Expect to work quite autonomously at times, and in close collaboration at others.
Details
Communication is key. Be prepared to document code, add comments where appropriate, and spend extra time on your PRs and tickets.

Contribute back to open source

InterNACHI® relies heavily on open-source projects, and we contribute back regularly (including to Laravel core). We have also published dozens of open-source packages—with over 4,000 GitHub stars and over 1 billion downloads.

Here are some we’re particularly proud of:

InterNACHI/modular

Modularize your Laravel application

1204

glhd/aire

Modern form builder for Laravel

543

glhd/gretel

Laravel breadcrumbs right out of a fairy tale

272

glhd/conveyor-belt

All the underlying mechanics necessary to write artisan commands that process lots of data efficiently

150

glhd/linen

Lightweight spreadsheets for Laravel

217

inxilpro/node-app-root-path

Determine the root path to your project

603

glhd/alpine-wizard

Multi-step wizard helpers for Alpine.js

156

Contribute back to the PHP community

InterNACHI® also contributes back to the PHP community financially, through GitHub sponsorships, Open Collective, etc. We are currently sponsoring individuals like Caleb Porzio and Graham Campbell, groups like Spatie and Larabelles, and PHP itself via monthly donations to the PHP Foundation.

We also love our local meetups!

PHP×Philly

Chris organizes PHP×Philly and created the PHP× network of meetups to help others run in-person PHP user groups around the world. PHP×Philly is a bi-monthly(ish) meetup for web artisans in the Philadelphia area.

PHP×STL

Skyler joined PHP× and started PHP×STL in 2024—a Saint Louis meetup for PHP artisans who want to connect.

UpstatePHP

Bogdan joined UpstatePHP when he moved to Greenville, SC and took over as a co-organizer soon after. UpstatePHP's mission is to empower PHP developers through knowledge sharing, collaboration, and community building in the Upstate of South Carolina!

We’re serious about testing

Write code with confidence, knowing that you have over 84,000 lines of tests to help guard you from breaking things.

This lets new developers contribute meaningful code to production sooner, and saves everyone from dreaded “something is wrong” texts on a Friday night.

Tests\Project\DebugTest
✓ no dd or dump calls
Tests\Project\FixMeTest
✓ project has no fixme comments
InterNACHI\Forms\Tests\Unit\ComponentPhpTsParityTest
✓ every ts component has a php class
✓ every php component has a ts file
Tests\Feature\Auth\RegisterTest
✓ can register as guest with membership intent
✓ california agents must provide drivers license number
✓ password must not fail haveibeenpwned
✓ user cannot register if email already exists
✓ cannot register with email as phone
✓ cannot register with email as given name
Tests\Feature\FalsePastDueNotificationsRegressionTest
✓ that a subscription ends at timestamp supersedes its transactions pays through function
✓ a plans period end is rounded up on a date within one day of the end of the period
✓ a plans period end meets expectations under typical conditions — leap year
Tests\Feature\Models\CompanyTest
✓ has subscription after active subscription is false if no future subscription
✓ has subscription after active subscription is true if future subscription has no end
✓ has subscription after active subscription is false if future subscription starts after active ends
✓ is past due accounts for recent charges — next payment past with pending payment late but in grace period
✓ is past due accounts for recent charges — next payment past with fully refunded payment
✓ an exception is triggered if is past due is called when timestamp is null
✓ it accounts for the first day of a new subscription
Tests\Feature\StrictSubscriptionTest
✓ paid until is affected by paper subscriptions
✓ future subscription without ends at does not affect paid until
✓ paid until respects trial ends at timestamp
✓ legacy renewal dates do not affect paid until
✓ expected last billed date logic during a leap year
✓ expected last billed date logic after a leap year
✓ paid until is set to subscription starts at initially
Tests\Feature\Jobs\DowngradeUserTest
✓ it sets the downgraded users role to guest
✓ it also downgrades associated member
✓ account users are notified when account manager is downgraded
✓ prevent mass subscription deletions
✓ when downgrading a user their plan is locked in for 6 months
✓ it ends subscriptions on the company next payment timestamp when past
✓ it ends subscriptions now when next payment at is future
Tests\Feature\Jobs\UpgradeUserTest
✓ it only evaluates program enrollments once
✓ it should restore previous public id
✓ it does not add unnecessary notes if role does not change
✓ should restore role for past member within 1 year
✓ should not restore role for past member after 1 year
✓ should keep affidavit if within 1 year
✓ should restore revoked enrollments within 1 year
✓ should not restore revoked enrollments after 1 year
Tests\Feature\Jobs\ProcessTransactionTest
✓ logs raw transaction to database
✓ sends notice on failed transaction from webhook
✓ does not send notice on failed transaction when reprocessed
✓ updates next payment at on refunded transaction — subscription starts at
✓ does not update next payment when active subscription is not current subscription
✓ downgrade user job is queued on a chargeback
✓ account id is still added when there is no subscription
Tests\Feature\Users\UserAbilityTest
✓ the database is only queried once
✓ it works with laravel policies
✓ it works with laravel gates
✓ casting to string provides a useful description of the permissions set
✓ user abilities are removed when a user is downgraded
InterNACHI\Exams\Tests\Aggregates\ExamSessionAggregateTest
✓ the current locale is stored on start
✓ localized text is stored on question answered event
✓ you cannot answer a question that was not allocated to the session
✓ submitting an answer after the sessions end triggers ending the session
✓ you cannot submit multiple answers to a single question
✓ you can submit multiple answers to a multi question
InterNACHI\Exams\Tests\ExamSessionScoreTest
✓ it scores single response questions with one correct choice
✓ it honors custom pass grades
✓ it scores single response questions with multiple differently correct choices
✓ it scores multiple response questions with multiple correct choices
✓ changing a response updates the score appropriately
✓ given multiple choice question with all incorrect and no correct it scores properly
InterNACHI\Exams\Tests\Integration\OnlineInspectorExamSessionTest
✓ that exam start page loads
✓ the results page loads even if no questions were answered
✓ you cannot view a question after the time limit has ended
✓ you cannot answer questions after the session time limit has ended
✓ you cannot access a session that you did not start
✓ you cannot change the exam via the url
✓ that exam start page loads does not offer to continue an ended session
InterNACHI\Exams\Tests\QuestionGroupTest
✓ it allocates questions that have not been seen yet
✓ it reuses previously seen questions after it allocates the total amount
✓ question allocation prioritizes iteration number
✓ question allocation is isolated between users
✓ question allocation works without user context
✓ handles question pool exactly matching exam size
InterNACHI\Agreements\Tests\Feature\Frontend\ElectronicSignatureWorkflowsTest
✓ single client can complete signature flow
✓ multi party signing with client and authorized representative
✓ agent can sign with clients email
✓ invalid signature attempts are rejected
✓ cannot re sign after initial signature
✓ expired signature url is rejected
✓ cannot modify client name when signing agreement
✓ user agent can be missing
InterNACHI\Agreements\Tests\Feature\Frontend\AgreementViewingTest
✓ unsigned agreement shows not signed message
✓ signed agreement shows signature details
✓ removed agreement returns agreement removed view
✓ signed routes required for viewing
✓ expired signed url is rejected
✓ tampered signed url is rejected
InterNACHI\Billing\Tests\Http\Controllers\My\Company\StripeCheckoutControllerTest
✓ shows plan selection form when company has no stripe subscription
✓ store creates checkout session and fires event
✓ shows update billing heading when company has active subscription
✓ shows almost finished heading for contractor during signup
✓ cannot create subscription more than 12 months in the future
✓ store rejects plan not in available plans
✓ default plan matches lock in plan
✓ shows grandfathered rate for cheaper monthly plan
✓ store accepts coupon code case insensitively
✓ store rejects coupon not valid for selected plan
✓ hides coupon input for paying members
InterNACHI\Billing\Tests\Http\Controllers\My\Company\ReplayTests
✓ happy path
✓ disputes
✓ switching from stripe to another gateway
InterNACHI\Billing\Tests\Feature\LifetimeMemberDiscountTest
✓ calculates tiered cost for each lifetime member
✓ skips sync when discount unchanged
✓ lifetime member added to subscription skips proration
✓ regular member added to subscription gets prorated
✓ consolidation does not cancel lifetime member subscriptions
✓ lifetime members do not receive payment emails on invoice payment
InterNACHI\Billing\Tests\Http\Controllers\Webhooks\StripeWebhookControllerTest
✓ it logs a valid webhook
Tests\Feature\Billing\CheckExpiredSubscriptionTest
✓ it downgrades a user with an expired subscription
✓ it doesnt downgrade a user with an active subscription
✓ it alerts admins if it would have downgraded an account with only future subscriptions
✓ it does not downgrade a user during their trial period
✓ it alerts admins if it finds an account owner that is also an company user
✓ it doesnt downgrade a user within their grace period
Tests\Feature\Models\CompanyPlanManagerTest
✓ company available plan gives active plan
✓ locked in plans are available to company
✓ locking in a new plan deletes existing lock in and creates a new one
✓ locking in the same plan reuses existing lock in
✓ when a lock in expires the plan is no longer available
Tests\Feature\My\Education\HasEducationRecordsTest
✓ it groups records by ce year rather than calendar year
✓ it handles feb 29 on a leap year
✓ leap year after february
InterNACHI\Forms\Tests\Unit\FormValidatorTest
✓ conditional fields excluded when rule evaluates to discard
✓ conditional fields included when rule evaluates to visible
✓ discarded conditional field does not cause validation failure
✓ strip discarded data removes discarded fields
✓ strip discarded data preserves hidden fields
✓ strip discarded data preserves compat fields when compatibility metadata present
✓ strip discarded data discards compat fields when compatibility metadata absent
InterNACHI\Forms\Tests\Feature\FormAutosaveTest
✓ autosave saves data to submission
✓ autosave overwrites previous autosave data
✓ autosave returns 409 on spec version mismatch
✓ full save clears autosave data
✓ edit page merges autosave data over saved data
✓ user cannot autosave another users submission
InterNACHI\Forms\Tests\Unit\SpecSchemaTest
✓ existing spec validates against schema
✓ rejects spec missing form name
✓ rejects empty children array
✓ rejects invalid key format
✓ rejects invalid rule effect
✓ accepts node with rules and effects
✓ rejects additional properties on root
InterNACHI\Msfh\Tests\Feature\Florida1802ToLegacyDataConverterTest
✓ converts building code answer a
✓ converts building code answer c to sfbc
✓ converts roof to wall clips purpose made 386 with compat
✓ returns null for roof to wall when required compat field missing
✓ redistributes z counts with compat grades
✓ round trip produces valid dto
InterNACHI\Rosters\Tests\Feature\Requirements\DepartmentHooksRequirementTest
✓ excludes session during north carolina real estate blackout period
✓ excludes session on north carolina real estate blackout start date
✓ allows session before north carolina real estate blackout period
✓ allows session after north carolina real estate blackout period
InterNACHI\Rosters\Tests\Feature\Requirements\SeatTimeRequirementTest
✓ allows session when no seat time requirement is set
✓ allows session when seat time exceeds requirement
✓ excludes session when seat time is below requirement
✓ calculates seat time correctly with exam time and education time
✓ rounds education seconds up to next minute
InterNACHI\Licensing\Tests\SyncLegacyLicenseTest
✓ creating a license creates a legacy one
✓ creating licenses in multiple departments in same state updates same legacy
✓ creating a license preserves other legacy licenses in same administrative area
✓ if all numbers are deleted the legacy record is deleted
✓ it handles national licenses when admin area is null
✓ syncing a modern license to legacy preserves oldest timestamp
InterNACHI\Certificates\Tests\CourseCertificateControllerTest
✓ it wont generate a certificate for a failed exam
✓ it renders a basic course certificate
✓ it handles florida licensing exam modifications
✓ it shows virginia certification number rather than license number
✓ it wont apply modifications for an inactive approval
✓ california specific intro and outro
✓ illinois licensing certificate renders properly
Tests\Feature\Admin\MergeUserTest
✓ admins can merge a user into another user
✓ when merging users education state is migrated
✓ when merging users license numbers are migrated
✓ when user is migrated the old public id is merged into the newer account
✓ non admins cannot merge a user into another user
✓ a user cannot be merged into themself
✓ a company owner cannot be merged
InterNACHI\ProgramRequirements\Tests\CpiHelperTest
✓ it applies extension to due date
✓ existing cpi qualifies for restoration
✓ cpi restoration grace period window
✓ missed cpi restoration grace period always returns false for new users
Tests\Feature\Projectors\MembershipPeriodProjectorTest
✓ it creates a membership period when role changes
✓ it closes previous period when role changes
✓ it skips imported events
✓ it handles multiple transitions
✓ it skips duplicate role transitions
✓ it skips events for nonexistent users
InterNACHI\OutboundEmails\Tests\SendGridWebhookTest
✓ events — processed event
✓ events — bounce event
✓ events — a user id is inferred by email address
✓ events — user id is preferred over email address
✓ events — discard events that are missing a sg message id
Tests\Feature\Middleware\RedirectMiddlewareTest
✓ it does not redirect a regular request
✓ it does redirect when a redirect exists
✓ only permanently redirects after a grace period
Tests\Feature\Models\RedirectTest
✓ creating a redirect for an existing url updates existing redirects
✓ circular redirects trigger an exception
InterNACHI\Events\Tests\Feature\Convention\ConventionCancellationTest
✓ registered user can cancel event within 90 days of registration
✓ user cannot cancel after 90 days of registration
✓ user cannot cancel within 30 days of event
✓ custom refund window is respected
InterNACHI\Scheduling\Tests\Features\JobCountTest
✓ company users only count their own jobs
✓ company managers count all jobs for the company
✓ company count includes former employees
✓ company count does not include jobs for a user before they became an employee
✓ a user sees all of their jobs before and after employment
Tests\Project\StripeIntegrationGroupTest
✓ files using stripe traits declare stripe integration group
InterNACHI\Exams\Tests\Browser\OnlineInspectorExamSystemTest
✓ answering all questions correctly passes the exam
✓ failing a single category fails the exam
✓ answering all questions incorrectly fails the exam
InterNACHI\Forms\Tests\Browser\FormSubmissionTest
✓ authenticated user sees form fields
✓ fill and save creates a database record and redirects to edit with data pre-filled
✓ conditional field is visible when rule condition is met
✓ conditional field is hidden when rule condition is not met
InterNACHI\Billing\Tests\Browser\StripeCheckoutCreditCardHappyPathTest
✓ guest user can complete stripe checkout for yearly subscription
✓ existing user can complete stripe checkout for subscription
InterNACHI\Uploads\Tests\Browser\FilePondTest
✓ it handles single uploads
✓ it converts heic uploads to jpeg
✓ it handles multiple uploads
✓ you can revert a pending upload
✓ old input is restored on error

…and continuous delivery

We’ve invested heavily in continuous delivery so that we can ship great features quickly. No need to wait for someone to deploy your feature, or delaying for a release day—when a PR is ready, it’s automatically deployed to production.

  • PHPUnit tests run on GitHub Actions
  • Pest browser tests run on GitHub Actions
  • Code style is checked with StyleCI
  • Assets are automatically built & deployed to CDN
  • Application deployments run via Laravel Envoyer
Some checks haven’t completed yet
0% complete running checks
@github-actions
Unit Tests
Waiting…
@github-actions
Browser Tests
Waiting…
@github-actions
Deploy Sidecar Functions
Waiting…
@github-actions
Build Assets
Waiting…
Style CI
Waiting…
Required statuses must pass before merging

All required statuses and check runs on this pull request must run successfully to enable automatic merging.

…and, just generally, about developer experience

Working on a page in the app? Our Cmd + K command palette has an option to jump straight to the view you're on. Need to set up the app locally? Run php artisan dev:setup for step-by-step help getting everything you need in place. Not sure which of three viable APIs you should use for a specific Laravel feature? Our bespoke linter will flag it before it gets to code review if you pick the wrong one. Working on an Alpine.js feature? We maintain the PhpStorm plugin for that. Etc. Etc.

Of course, there's always room for improvement (maybe you’ll have some ideas on how to help!)—but in general know that you’d be working with a team that cares.

Thanks for your interest!

We've filled our current software developer opening. We're not actively hiring right now, but we're always glad to hear from great people — check back here for future roles.

InterNACHI® is an equal opportunity employer and all qualified applicants will receive consideration for employment without regard to race, color, religion, sex, sexual orientation, gender identity, national origin, disability status, protected veteran status, or any other characteristic protected by law.