{"id":13582482,"url":"https://github.com/short-d/short","last_synced_at":"2025-09-27T13:30:47.106Z","repository":{"id":41575454,"uuid":"195310249","full_name":"short-d/short","owner":"short-d","description":"URL shortening service written in Go and React","archived":true,"fork":false,"pushed_at":"2020-09-13T17:51:22.000Z","size":23126,"stargazers_count":875,"open_issues_count":56,"forks_count":149,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-01-17T11:11:55.353Z","etag":null,"topics":["clean-architecture","codeclimate","codecov","continuous-delivery","datadog","gitops","go","grpc","ipstack","kubernetes","microservices-architecture","oauth","postgresql","react","recaptcha","scss","segement","solid-principles","typescript","unit-testing"],"latest_commit_sha":null,"homepage":"https://short-d.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/short-d.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2019-07-04T23:20:08.000Z","updated_at":"2025-01-08T21:17:58.000Z","dependencies_parsed_at":"2022-07-09T14:17:44.386Z","dependency_job_id":null,"html_url":"https://github.com/short-d/short","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/short-d%2Fshort","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/short-d%2Fshort/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/short-d%2Fshort/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/short-d%2Fshort/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/short-d","download_url":"https://codeload.github.com/short-d/short/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234438053,"owners_count":18832617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["clean-architecture","codeclimate","codecov","continuous-delivery","datadog","gitops","go","grpc","ipstack","kubernetes","microservices-architecture","oauth","postgresql","react","recaptcha","scss","segement","solid-principles","typescript","unit-testing"],"created_at":"2024-08-01T15:02:45.478Z","updated_at":"2025-09-27T13:30:40.908Z","avatar_url":"https://github.com/short-d.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Short\n\n[![Build Status](https://ci.time4hacks.com/api/badges/short-d/short/status.svg)](https://ci.time4hacks.com/short-d/short)\n[![codecov](https://codecov.io/gh/short-d/short/branch/master/graph/badge.svg)](https://codecov.io/gh/short-d/short)\n[![Maintainability](https://api.codeclimate.com/v1/badges/910f974653f1b3495534/maintainability)](https://codeclimate.com/github/short-d/short/maintainability)\n[![Go Report Card](https://goreportcard.com/badge/github.com/short-d/short)](https://goreportcard.com/report/github.com/short-d/short)\n[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/byliuyang/short)\n[![Floobits Status](https://floobits.com/byliuyang/short.svg)](https://floobits.com/byliuyang/short/redirect)\n\n![Demo](promo/marquee.png)\n\n## Preview\n\n![Demo](doc/demo.gif)\n\n## Get `s/` Chrome extension\n\nInstall it from [Chrome Web Store](https://short-d.com/r/ext) or build it\nfrom [source](https://short-d.com/r/ext-code)\n\n## Dependent Projects\n\n- [app](https://github.com/short-d/app): Reusable framework for Go apps \u0026 command\n   line tools.\n- [kgs](https://github.com/short-d/kgs): Offline unique key generation service.\n\n## Table of Contents\n\n1. [Getting Started](#getting-started)\n   1. [Accessing the source code](#accessing-the-source-code)\n   1. [Prerequisites](#prerequisites)\n   1. [Local environmental variables](#local-environmental-variables)\n   1. [Create reCAPTCHA account](#create-recaptcha-account)\n   1. [Configure Single Sign On](#configure-single-sign-on)\n   1. [Backend](#backend)\n   1. [Frontend](#frontend)\n1. [System Design](#system-design)\n   1. [App Level Architecture](#app-level-architecture)\n   1. [Service Level Architecture](#service-level-architecture)\n   1. [Object Oriented Design](#object-oriented-design)\n   1. [Dependency Injection](#dependency-injection)\n   1. [Database Modeling](#database-modeling)\n   1. [Feature Toggle](#feature-toggle)\n   1. [Search Engine Optimization](#search-engine-optimization)\n   1. [Social Media Summary Card](#social-media-summary-card)\n1. [Testing](#testing)\n   1. [The Importance Of Automation](#the-importance-of-automation)\n   1. [Testing Strategy](#testing-strategy)\n   1. [Unit Testing](#unit-testing)\n   1. [Integration Testing](#integration-testing)\n   1. [Component Testing](#component-testing)\n   1. [Contract Testing](#contract-testing)\n   1. [End To End Testing](#end-to-end-testing)\n   1. [The Test Pyramid](#the-test-pyramid)\n1. [Deployment](#deployment)\n   1. [Continuous Delivery](#continuous-delivery)\n   1. [Kubernetes](#kubernetes)\n   1. [GitOps](#gitops)\n1. [Tools We Use](#tools-we-use)\n1. [Contributing](#contributing)\n1. [Author](#author)\n1. [License](#license)\n\n## Getting Started\n\n### Accessing the source code\n\n```bash\ngit clone https://github.com/short-d/short.git\n```\n\n### Prerequisites\n\n- [Go](https://golang.org/doc/install) v1.13.1\n- [Node.js](https://nodejs.org/en/download/) v12.12.0\n- [Yarn](https://classic.yarnpkg.com/en/docs/install) v1.19.1\n- [PostgreSQL](doc/tutorial/POSTGRES.md) v12.0\n\n### Local environmental variables\n\n1. Copy `backend/.env.dist` file to `backend/.env`:\n\n   ```bash\n   cp backend/.env.dist backend/.env\n   ```\n\n1. Copy `frontend/.env.development.dist` file to `frontend/.env.development`:\n\n   ```bash\n   cp frontend/.env.development.dist frontend/.env.development\n   ```\n\n\n### Create reCAPTCHA account\n\n1. Sign up at [ReCAPTCHA](https://short-d.com/r/recaptcha) with the\n   following configurations:\n\n   | Field           | Value          |\n   |-----------------|----------------|\n   | Label           | `Short`        |\n   | reCAPTCHA type  | `reCAPTCHAv3`  |\n   | Domains         | `localhost`    |\n\n1. Open `settings`. Copy `SITE KEY` and `SECRET KEY`.\n\n1. Replace the value of `RECAPTCHA_SECRET` in the `backend/.env` file with\n   `SECRET KEY`.\n1. Replace the value of `REACT_APP_RECAPTCHA_SITE_KEY` in\n   `frontend/.env.development` file with `SITE KEY`.\n\n### Configure Single Sign On\n#### Google\n\nCreate a new Client ID at\n   [Google API Credentials](https://console.developers.google.com/apis/credentials):\n\n1. Click on `Create Credentials` and select `OAuth client ID`.\n\n1. Select `Web application` for `Application type`.\n\n1. Fill in `http://localhost/oauth/google/sign-in/callback` for `Authorized redirect URIs` and click on `Create`.\n \n1. Replace the value of `GOOGLE_CLIENT_ID` in `backend/.env` file with `Your Client ID`.\n1. Replace the value of `GOOGLE_CLIENT_SECRET` in `backend/.env` file with\n   `Your Client Secret`.\n\n#### Facebook\nYou can find the detailed instructions on setting up Facebook sign in [here](doc/sso/FACEBOOK.md) in case you are interested in. \n\n#### Github\nYou can find the detailed instructions on setting up Github sign in [here](doc/sso/GITHUB.md) in case you are interested in.\n   \n### Backend\n\n1. Update placeholder values with your own configurations.\n\n1. Launch backend server\n\n   ```bash\n   cd backend\n   ./scripts/dev\n   ```\n\n1. Remember to install developers tools before start coding:\n\n   ```bash\n   ./scripts/tools\n   ```\n\n### Frontend\n\n1. Update `REACT_APP_RECAPTCHA_SITE_KEY` in `frontend/.env.development`.\n\n1. Launch frontend server\n\n   ```bash\n   cd frontend\n   ./scripts/dev\n   ```\n\n1. Visit [http://localhost:3000](http://localhost:3000)\n\n## System Design\n\n### App Level Architecture\n\nShort backend is built on top of\n[Uncle Bob's Clean Architecture](https://api.short-d.com/r/ca), the central\nobjective of which is separation of concerns.\n\n![Short Backend](doc/clean-architecture/short-backend.jpg)\n\nIt enables the developers to modify a single component of the system at a time\nwhile leaving the rest unchanged. This minimizes the amount of changes have to\nbe made in order to support new requirements as the system grows. Clean\nArchitecture also improves the testability of system, which in turn saves\nprecious time when creating automated tests.\n\n### Service Level Architecture\n\nShort adopts [Microservices Architecture](https://api.short-d.com/r/ms) to\norganize dependent services around business capabilities and to enable\nindependent deployment of each service.\n\n![Short Cloud](doc/cloud/overall.jpg)\n[SSR](https://docs.google.com/document/d/16iV91aESfnYU6rIEWGEzws3nbDX3hB-St9gAxrtCAa8), \n[Toggle](https://docs.google.com/document/d/1TuWexeKwhQh8JTytRAwST3XujBi0wTGExwJan-WfXWs),\n[Status Page](https://docs.google.com/document/d/1pgRNnD8yAlEmj-sucS_FZ89LdvBy5zpKQ9OvILoBqDM), Search,\n[Data Reporter](https://docs.google.com/document/d/1-BtxBuS4zIk8H1oXDe-qqEccWp4v6aT2GrWBfwIX5oI),\n[Feedback Widget](https://docs.google.com/document/d/1IoaTMHsOi5Tb0ZV4btxsvUnKplKi2lxaIYU600cwRuc),\n and Cloud API are still under active development.\n\n### Object Oriented Design\n\nShort leverages class design, package cohesion, and package coupling principles\nto manage logical dependency between internal components.\n\n#### Class Design\n\n| Principal                                                        | Description                                                            |\n|------------------------------------------------------------------|------------------------------------------------------------------------|\n| [Single Responsibility Principle](https://api.short-d.com/r/srp) | A class should have one, and only one, reason to change.               |\n| [Open Closed Principle](https://api.short-d.com/r/ocp)           | You should be able to extend a classes behavior, without modifying it. |\n| [Liskov Substitution Principle](https://api.short-d.com/r/lsp)   | Derived classes must be substitutable for their base classes.          |\n| [Interface Segregation Principle](https://api.short-d.com/r/isp) | Make fine grained interfaces that are client specific.                 |\n| [Dependency Inversion Principle](https://api.short-d.com/r/dip)  | Depend on abstractions, not on concretions.                            |\n\n#### Package Cohesion\n\n| Principal                                                            | Description                                           |\n|----------------------------------------------------------------------|-------------------------------------------------------|\n| [Release Reuse Equivalency Principle](https://api.short-d.com/r/rep) | The granule of reuse is the granule of release.       |\n| [The Common Closure Principle](https://api.short-d.com/r/ccp)        | Classes that change together are packaged together.   |\n| [The Common Reuse Principle](https://api.short-d.com/r/crp)          | Classes that are used together are packaged together. |\n\n#### Package Coupling\n\n| Principal                                                       | Description                                           |\n|-----------------------------------------------------------------|-------------------------------------------------------|\n| [Acyclic Dependencies Principle](https://api.short-d.com/r/adp) | The dependency graph of packages must have no cycles. |\n| [Stable Dependencies Principle](https://api.short-d.com/r/sdp)  | Depend in the direction of stability.                 |\n| [Stable Abstractions Principle](https://api.short-d.com/r/sap)  | Abstractness increases with stability.                |\n\n### Dependency Injection\n\nShort produces flexible and loosely coupled code, by explicitly providing\ncomponents with all of the dependencies they need.\n\n```go\ntype Authenticator struct {\n  tokenizer          fw.CryptoTokenizer\n  timer              fw.Timer\n  tokenValidDuration time.Duration\n}\n\nfunc NewAuthenticator(\n  tokenizer fw.CryptoTokenizer,\n  timer fw.Timer,\n  tokenValidDuration time.Duration,\n) Authenticator {\n  return Authenticator{\n    tokenizer:          tokenizer,\n    timer:              timer,\n    tokenValidDuration: tokenValidDuration,\n  }\n}\n```\n\nShort also simplifies the management of the big block of order-dependent\ninitialization code with [Wire](https://api.short-d.com/r/wire), a compile time\ndependency injection framework by Google.\n\n```go\nfunc InjectGraphQlService(\n  name string,\n  sqlDB *sql.DB,\n  graphqlPath provider.GraphQlPath,\n  secret provider.ReCaptchaSecret,\n  jwtSecret provider.JwtSecret,\n  bufferSize provider.KeyGenBufferSize,\n  kgsRPCConfig provider.KgsRPCConfig,\n  tokenValidDuration provider.TokenValidDuration,\n) (mdservice.Service, error) {\n  wire.Build(\n    wire.Bind(new(fw.GraphQlAPI), new(graphql.Short)),\n    wire.Bind(new(url.Retriever), new(url.RetrieverPersist)),\n    wire.Bind(new(url.Creator), new(url.CreatorPersist)),\n    wire.Bind(new(repo.UserURLRelation), new(db.UserURLRelationSQL)),\n    wire.Bind(new(repo.URL), new(*db.URLSql)),\n    wire.Bind(new(keygen.KeyGenerator), new(keygen.Remote)),\n    wire.Bind(new(service.KeyFetcher), new(kgs.RPC)),\n\n    observabilitySet,\n    authSet,\n\n    mdservice.New,\n    provider.NewGraphGophers,\n    mdhttp.NewClient,\n    mdrequest.NewHTTP,\n    mdtimer.NewTimer,\n\n    db.NewURLSql,\n    db.NewUserURLRelationSQL,\n    provider.NewRemote,\n    url.NewRetrieverPersist,\n    url.NewCreatorPersist,\n    provider.NewKgsRPC,\n    provider.NewReCaptchaService,\n    requester.NewVerifier,\n    graphql.NewShort,\n  )\n  return mdservice.Service{}, nil\n}\n```\n\n### Database Modeling\n\n![Entity Relation Diagram](doc/db/er.jpg)\n\n### Feature Toggle\n\nShort employs `feature toggles` to modify system behavior without changing code.\nUI components controlled by the feature toggles are created inside a centralized\n`UIFactory` in order to avoid having nested `if` `else` statement across the\ncode base:\n\n```typescript\n// UIFactory.tsx\nexport class UIFactory {\n  constructor(\n    private featureDecisionService: IFeatureDecisionService\n  ) {}\n\n  public createGoogleSignInButton(): ReactElement {\n    if (!this.featureDecisionService.includeGoogleSignButton()) {\n      return \u003cdiv /\u003e;\n    }\n    return (\n      \u003cGoogleSignInButton\n        googleSignInLink={this.authService.googleSignInLink()}\n      /\u003e\n    );\n  }\n\n  public createGithubSignInButton(): ReactElement {\n    if (!this.featureDecisionService.includeGithubSignButton()) {\n      return \u003cdiv /\u003e;\n    }\n    return (\n      \u003cGithubSignInButton\n        githubSignInLink={this.authService.githubSignInLink()}\n      /\u003e\n    );\n  }\n}\n```\n\nShort also provides `IFeatureDecisionService` interface, allowing the developers\nto switch to dynamic feature toggle backend in the future by simply swapping\nthe dependency injected.\n\n```typescript\n// FeatureDecision.service.ts\nexport interface IFeatureDecisionService {\n  includeGithubSignButton(): boolean;\n  includeGoogleSignButton(): boolean;\n  includeFacebookSignButton(): boolean;\n}\n```\n\n```typescript\n// StaticConfigDecision.service.ts\nimport { IFeatureDecisionService } from './FeatureDecision.service';\n\nexport class StaticConfigDecisionService implements IFeatureDecisionService {\n  includeGithubSignButton(): boolean {\n    return false;\n  }\n  includeGoogleSignButton(): boolean {\n    return false;\n  }\n  includeFacebookSignButton(): boolean {\n    return true;\n  }\n}\n```\n\n```typescript\n// dep.ts\nexport function initUIFactory(\n  ...\n): UIFactory {\n  ...\n  const staticConfigDecision = new StaticConfigDecisionService();\n  ...\n  return new UIFactory(\n    ...,\n    staticConfigDecision\n  );\n}\n```\n\nYou can read about the detailed feature toggle design on\n[this article](https://martinfowler.com/articles/feature-toggles.html).\n\n### Search Engine Optimization\n\nIn order to improve the quality and quantity of the website's traffic, Short\nincreases its visibility to web search engines through HTML meta tags.\n\n```html\n\u003c!-- ./frontend/public/index.html --\u003e\n\u003ctitle\u003eShort: Free online link shortening service\u003c/title\u003e\n\n\u003c!-- Search Engine Optimization --\u003e\n\u003cmeta name=\"description\"\n      content=\"Short enables people to type less for their favorite web sites\"\u003e\n\u003cmeta name=\"robots\" content=\"index, follow\"\u003e\n\u003clink href=\"https://short-d.com\" rel=\"canonical\"\u003e\n```\n\nIf you search `short-d.com` on Google, you should see Short shows up as\nthe first result:\n\n![Google Search Result](doc/seo/google.jpg)\n\n### Social Media Summary Card\n\n#### Facebook \u0026 LinkedIn\n\nShort leverages `Open Graph` tags to control what content shows up in\nthe summary card when the website is shared on Facebook or LinkedIn:\n\n```html\n\u003c!-- ./frontend/public/index.html --\u003e\n\u003c!-- Open Graph --\u003e\n\u003cmeta property=\"og:title\" content=\"Short: Free link shortening service\"/\u003e\n\u003cmeta property=\"og:description\"\n      content=\"Short enables people to type less for their favorite web sites\"/\u003e\n\u003cmeta property=\"og:image\"\n      content=\"https://short-d.com/promo/small-tile.png\"/\u003e\n\u003cmeta property=\"og:url\" content=\"https://short-d.com\"/\u003e\n\u003cmeta property=\"og:type\" content=\"website\"/\u003e\n```\n\nShared on Facebook:\n\n![Facebook Card](doc/social-media-card/facebook.jpg)\n\nShared on LinkedIn:\n\n![LinkedIn Card](doc/social-media-card/linkedin.jpg)\n\n#### Twitter\n\nTwitter uses its own meta tags to determine what will show up when\nthe website is mentioned in a Tweet:\n\n```html\n\u003c!-- Twitter --\u003e\n\u003cmeta name=\"twitter:card\" content=\"summary_large_image\"/\u003e\n\u003cmeta name=\"twitter:site\" content=\"@byliuyang11\"/\u003e\n\u003cmeta name=\"twitter:title\" content=\"Short: Free link shortening service\"/\u003e\n\u003cmeta name=\"twitter:description\"\n      content=\"Short enables people to type less for their favorite web sites\"/\u003e\n\u003cmeta name=\"twitter:image\" content=\"https://short-d.com/promo/twitter-card.png\"/\u003e\n```\n\n![Twitter Card](doc/social-media-card/twitter.jpg)\n\n## Testing\n\n### The Importance Of Automation\n\nShort is maintained by a small team of talented software engineers working\nat Google, Uber, and Vmware as a side project. The team wants to deliver new\nfeatures faster without sacrificing its quality. Testing ever-increasing\namount of features manually soon becomes impossible — unless we want\nto spend all our time with manual, repetitive work instead of delivering\nworking features.\n\nTest automation is the only way forward.\n\n### Testing Strategy\n\n![Test Strategy](doc/testing/test-strategy.png)\n\nPlease read [Testing Strategies in a Microservice Architecture](https://martinfowler.com/articles/microservice-testing)\nfor a detailed introduction on test strategies.\n\n### Unit Testing\n\nA unit test exercises the smallest piece of testable software in the\napplication to determine whether it behaves as expected.\n\n![Unit Test](doc/testing/unit-test.png)\n\nRun unit tests for backend:\n\n```bash\ncd backend\n./scripts/unit-test\n```\n\n#### Sociable And Solitary\n\n![Two Types of Unit Test](doc/testing/unit-test-two-types.png)\n\n#### The FIRST Principal\n\n- [F]ast: Unit tests should be fast otherwise they will slow down\n   development \u0026 deployment.\n- [I]ndependent: Never ever write tests which depend on other test cases.\n- [R]epeatable: A repeatable test is one that produces the same results\n   each time you run it.\n- [S]elf-validating: There must be no manual interpretation of the results.\n- [T]imely/[T]horoughly: Unit tests must be included for every pull request\n   of a new feature and cover edge cases, errors, and bad inputs.\n\n#### Test Structure\n\nAn automated test method should be composed of 3As: Arrange, Act, and Assert.\n\n- [A]rrange: All the data needed for a test should be arranged as part\n  of the test. The data used in a test should not depend on the environment\n  in which the test is running.\n- [A]ct: Invoke the actual method under test.\n- [A]ssert: A test method should test for a single logical outcome.\n\n### Integration Testing\n\nAn integration test verifies the communication paths and interactions\nbetween components to detect interface defects.\n\n![Integration Test](doc/testing/integration-test.png)\n\nRun integration tests for backend:\n\n```bash\ncd backend\n./scripts/integration-test\n```\n\n### Component Testing\n\nA component test limits the scope of the exercised software to a portion\nof the system under test, manipulating the system through internal code\ninterfaces and using test doubles to isolate the code under test from\nother components.\n\n#### In Process\n\n![Component Test](doc/testing/component-test-in-process.png)\n\n#### Out Of Process\n\n![Component Test](doc/testing/component-test-out-of-process.png)\n\n### Contract Testing\n\nAn integration contract test is a test at the boundary of an external\nservice verifying that it meets the contract expected by a consuming\nservice.\n\n### End To End Testing\n\nAn end-to-end test verifies that a system meets external requirements\nand achieves its goals, testing the entire system, from end to end.\n\n### The Test Pyramid\n\n![Test Pyramid](doc/testing/test-pyramid.png)\n\n## Deployment\n\n### Continuous Delivery\n![Continuous Delivery](doc/deployment/cd-pipeline.jpg)\n\nCurrently, we use continuous delivery to deploy code changes to staging \u0026 \nproduction environment. \n\nMerging pull request into master branch on Github will automatically deploy the\nchanges to [staging](https://staging.short-d.com). Merging from `master` branch\nto `production` branch will automatically deploy the latest code to the production.\n\nIn the future, when after we add enough automated tests, we may migrate to \ncontinuous deployment instead for faster releases.\n\nYou can find the differences between continuous delivery \u0026 continuous deployment [here](doc/tutorial/CI-CD.md)\n\n### Kubernetes\n\nShort leverages [Kubernetes](https://kubernetes.io) to automate deployment, scaling,\nand management of containerized microservices.\n\n![Node overview](https://d33wubrfki0l68.cloudfront.net/5cb72d407cbe2755e581b6de757e0d81760d5b86/a9df9/docs/tutorials/kubernetes-basics/public/images/module_03_nodes.svg)\n\n### GitOps\nShort uses [GitOps](https://github.com/byliuyang/gitops) to configure Kubernetes\ncluster and span up new services.\n\n![Git Ops](doc/deployment/gitops.jpg)\n\n## Tools We Use\n\n- [Drone](https://short-d.com/r/ci): Continuous integration\n  written in Go\n- [Code Climate](https://codeclimate.com/): Automated code\n  review\n- [ElephantSQL](https://www.elephantsql.com): Managed PostgreSQL service.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code\nof conduct, the process for submitting pull requests to us, and our code\nreview guideline.\n\n## Author\n\nHarry Liu - *Initial work* - [byliuyang](https://short-d.com/r/ghharry)\n\nAs the tech lead of Short, I am responsible for the overall planning, execution\nand success of complex software solutions to meet users' needs.\n\nI deeply believe in and am striving to achieve the right column of the\nfollowing diagram:\n\n![Manager vs Leader](doc/leader-vs-manager.jpg)\n\n## License\n\nThis project is maintained under MIT license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshort-d%2Fshort","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshort-d%2Fshort","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshort-d%2Fshort/lists"}