{"id":19972637,"url":"https://github.com/jpb06/tic-tac-toe","last_synced_at":"2026-03-07T00:01:54.253Z","repository":{"id":42013149,"uuid":"482037132","full_name":"jpb06/tic-tac-toe","owner":"jpb06","description":"Revisiting the good old tic tac toe example","archived":false,"fork":false,"pushed_at":"2026-03-05T10:01:12.000Z","size":821,"stargazers_count":4,"open_issues_count":11,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-05T14:33:16.528Z","etag":null,"topics":["nextjs","react","react-testing-library","tailwindcss","tic-tac-toe-game"],"latest_commit_sha":null,"homepage":"https://tic-tac-toe-lyart-five.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jpb06.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-04-15T17:38:07.000Z","updated_at":"2025-12-12T22:02:14.000Z","dependencies_parsed_at":"2026-03-05T12:03:33.703Z","dependency_job_id":null,"html_url":"https://github.com/jpb06/tic-tac-toe","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jpb06/tic-tac-toe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpb06%2Ftic-tac-toe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpb06%2Ftic-tac-toe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpb06%2Ftic-tac-toe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpb06%2Ftic-tac-toe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jpb06","download_url":"https://codeload.github.com/jpb06/tic-tac-toe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpb06%2Ftic-tac-toe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30204109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"ssl_error","status_checked_at":"2026-03-06T18:57:34.882Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nextjs","react","react-testing-library","tailwindcss","tic-tac-toe-game"],"created_at":"2024-11-13T03:08:45.803Z","updated_at":"2026-03-07T00:01:54.218Z","avatar_url":"https://github.com/jpb06.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tic-tac-toe\n\n[![Open in Visual Studio Code](https://img.shields.io/static/v1?logo=visualstudiocode\u0026label=\u0026message=Open%20in%20Visual%20Studio%20Code\u0026labelColor=2c2c32\u0026color=007acc\u0026logoColor=007acc)](https://github.dev/jpb06/tic-tac-toe)\n[![Front deployment](https://img.shields.io/github/deployments/jpb06/tic-tac-toe/production?label=front%20deploy\u0026logo=vercel\u0026logoColor=white)](https://tic-tac-toe-jpb06.vercel.app)\n![Github workflow](https://img.shields.io/github/actions/workflow/status/jpb06/tic-tac-toe/tests-scan.yml?branch=main\u0026logo=github-actions\u0026label=last%20workflow)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=code_smells)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=bugs)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=jpb06_tic-tac-toe\u0026metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=jpb06_tic-tac-toe)\n![Last commit](https://img.shields.io/github/last-commit/jpb06/tic-tac-toe?logo=git)\n\nToying again with this simple subject, using tailwind.\n\n\u003c!-- readme-package-icons start --\u003e\n\n\u003cp align=\"left\"\u003e\u003ca href=\"https://docs.github.com/en/actions\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/GithubActions-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://www.typescriptlang.org/docs/\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/TypeScript.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://nodejs.org/en/docs/\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/NodeJS-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://bun.sh/docs\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/Bun-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://biomejs.dev/guides/getting-started/\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/Biome-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://nextjs.org/docs/getting-started\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/NextJS-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://reactjs.org/docs/getting-started.html\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/React-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://swc.rs/docs/getting-started\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/Swc-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://tailwindcss.com/docs/installation\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/TailwindCSS-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://testing-library.com/docs/\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/TestingLibrary-Dark.svg\" /\u003e\u003c/a\u003e\u0026nbsp;\u003ca href=\"https://vitest.dev/guide/\" target=\"_blank\"\u003e\u003cimg height=\"50\" width=\"50\" src=\"https://raw.githubusercontent.com/jpb06/jpb06/master/icons/Vitest-Dark.svg\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\n\u003c!-- readme-package-icons end --\u003e\n\n## ⚡ Components tree\n\n![Diagram](./docs/components-tree.png)\n\nWe segregate here simple from complex components. We can adopt distinct testing strategies depending on the complexity of a component: unit tests for simple components and integration tests for complex ones.\n\n## ⚡ Complexity\n\nSo what makes a component complex? In the end, this is all about how hard it is to understand. A few factors that may influence this:\n\n### 🔶 Local state\n\nIs the component owning local state? This is the most common to distinguish components: stateless vs stateful. Historically, stateful components were called containers and were class components while stateless ones could be FCs. Of course React 16 changed all this but a lot of projects still use this containers vs components approach.\n\nDo note this includes owning state through its hooks.\n\n### 🔶 Server / Global state\n\nIs the component using external state?\n\n- By server state we mean cache that is reflecting interaction with our backend(s), managed by dedicated libraries. Take a look at [react-query](https://react-query.tanstack.com) or [swr](https://swr.vercel.app).\n- About global state, in most projects this will mean \"Is it connected to a redux store?\". On modern approaches this could mean stuff like \"Is it using jotai atoms?\". Redux is still widely used but I would not advise using it unless you **really** don't have another choice. More details [here](https://github.com/jpb06/workshop-react-front/blob/master/docs/bp-frontend-guidelines.md#-lets-avoid-redux-if-we-can).\n\n### 🔶 Rendering complexity\n\n- Is there a convoluted rendering workflow? Conditional rendering?\n- Is there side effects?\n- Is the component containing complex logic?\n\n### 🔶 Testability\n\nSimply put, how easy is the component to test?\n\n## ⚡ Guidelines\n\n### 🔶 Use FCs and hooks\n\nNo brainer here. Class components are done for!\n\n### 🔶 KISS : Keep It Simple, Stupid\n\nBetween two solution, the simplest one will always be better. Complexity means higher maintainability costs. Please. Do not overengineer stuffs 🥲.\n\nA source of complexity often comes from the tools we choose to answer a problem. For example, using redux middlewares like thunk/saga to deal with server state. There is better choices like swr or react-query for this!\n\n### 🔶 DRY : Don't Repeat Yourself\n\nWe hear that one every day as developers. Generally a good advice; use it rationally though. Functionnally divergent code is not a good candidate for factorization.\n\n### 🔶 YAGNI : You Ain't Gonna Need It\n\nAgain. Do not overengineer stuffs 🥲. Add only to the codebase what is strictly necessary to answer your need. The more code you add, the more code you will have to maintain.\n\n### 🔶 SSOT : Single Source Of Truth\n\nParts of the codebase can benefit from being centralized. Relates to DRY.\n\n### 🔶 Do one thing, do it well (Single Responsibility Principle)\n\nComponents / hooks / modules should have as few responsibilities as possible. Less responsibilities means less complexity and easier testing. Easier understanding for our next readers as well.\n\n### 🔶 Make it fit to the screen\n\nBy limiting the number of lines each unit of code can have, we mechanically enforce SRP. We make sure each unit is easy to understand.\n\n### 🔶 Maximize signal to noise ratio / convey intent\n\nOur goal is to maximize signal to noise radio to help our future readers understand and maintain easily our codebase. Every single line of code should be necessary and be clear about what it does. The original intent of the author should be clear.\n\n### 🔶 Naming is key\n\nSpliting the code into small units means we get great opportunities to convey intent to our future readers, by naming units of code with labels informing us about their purpose : what they do / are.\n\n### 🔶 Limit indentation\n\nTwo levels of indentation max (That's an if statement inside another if for example). One level max in react components.\n\nIndentation means higher cyclomatic complexity, which means higher complexity. We want to keep things simple.\n\nA few tricks can help us here:\n\n- Extract code. Limiting indentation gives us an opportunity to label things by extracting them.\n- Return early: if execution path can no longer continue (conditions unmet), simply return right away.\n- Fail fast and loud: if an error occurs, terminate immediately.\n\n### 🔶 The rule of seven / Functions: O2\n\nWhen we read code, our brain is the compiler. We have limited capabilities. For example, we cannot juggle with seven values at the same time (try it yourself). That is why we should limit the number of floating variables in a hook or a module. Functions parameters should be limited as well: one or two is ideal. It will limit the risk to give too many responsibilities to an unit of code.\n\n### 🔶 Avoid complex conditionals\n\nMore than two combined conditions in a if makes it very hard to evaluate the end result. If you have to combine conditions, extract them in variables. It will also help you conveying intent.\n\n### 🔶 Avoid magic numbers\n\nAdding a literal value does not inform the reader about the intent behind this decision. Creating a named constant gives you, again, an opportunity to convey intent.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpb06%2Ftic-tac-toe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjpb06%2Ftic-tac-toe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpb06%2Ftic-tac-toe/lists"}