Skip to content

Commit 545aac6

Browse files
committed
Updating design doc
* Add details of admin routes * Add scale section * Minor updates
1 parent 394a334 commit 545aac6

File tree

1 file changed

+85
-9
lines changed

1 file changed

+85
-9
lines changed

docs/design.md

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,109 @@ The following section details design decisions based on each product requirement
2525

2626
### Create Short URLs
2727

28-
ToDo
28+
Creating short URLs can be done via POST to the root. The only required parameter is `full_url` as empty
29+
`short_urls` get an auto generated code. Much more detail on this will be detailed later.
30+
31+
```bash
32+
$ curl -D - -X POST --url "/" --data "full_url=https://anytown.usa"
33+
HTTP/1.1 200 OK
34+
Content-Type: application/json; charset=utf-8
35+
36+
{"created_at":"2021-06-22T14:46:35Z","full_url":"https://anytown.usa","short_url":"Sa37mxEZilg-"}
37+
```
2938

3039
### Custom Slugs
3140

32-
ToDo
41+
Custom slugs can also be created by providing a `short_url` parameter to the create route.
42+
43+
```bash
44+
$ curl -D - -X POST --url "/" --data "full_url=https://anytown.usa" --data "short_url=any"
45+
HTTP/1.1 200 OK
46+
Content-Type: application/json; charset=utf-8
47+
48+
{"created_at":"2021-06-22T14:48:12Z","full_url":"https://anytown.usa","short_url":"any"}
49+
```
50+
51+
There are a few restrictions to what `short_url`s are valid. Only alpha numeric and `-`, `_` and `+` characters
52+
are allowed. Any uppercase letters are converted to lower case. The reason for this, which breaks down to
53+
security, is outlined in further detail below.
3354

3455
### Expire URLs
3556

36-
ToDo
57+
Expiring URLs is done through a DELETE HTTP request to the slug.
58+
59+
```bash
60+
$ curl -D - -X DELETE --url "/any"
61+
HTTP/1.1 200 OK
62+
Content-Type: application/json; charset=utf-8
63+
64+
{}
65+
```
3766

3867
### Get Redirected
3968

40-
This is the heart of the project. There are a few key considerations with this functionality.
69+
This is the heart of the project. To go from a short url to a full url, simply perform a GET to the short url.
70+
71+
```bash
72+
$ curl -D - -X GET --url "/any"
73+
HTTP/1.1 302 Found
74+
Location: https://anytown.usa
75+
Content-Type: text/html; charset=utf-8
76+
```
77+
78+
There are a few key considerations with this functionality.
4179

4280
1. This action should always be fast.
4381
2. We should prevent security concerns when possible.
82+
3. This design should scale reasonably well.
83+
84+
#### Speed
4485

4586
The first point can be achieved with a few techniques. Translations from short url to full url are done by a SQL
4687
query that should be covered by an index. This means the `short_url` field needs to be indexed. If we need
4788
further optimization, an in-memory cache could be used to enhance speed of "hot" slugs, but for now we will only
48-
be relying on the PostgreSQL cache.
89+
be relying on the PostgreSQL cache. Any non-local cache (memcached, Redis) _might_ be faster, but because they
90+
are across the network, it is possible that such a solution offers no additional performance while adding a
91+
decent amount of complexity.
92+
93+
#### Security
4994

5095
The second point has two pieces. The first is not allowing injection or poorly formed URLs by reducing the
5196
character set that is allowed. We only allow alphanumeric characters (`A-Z`, `a-z` and `0-9`) as well as
52-
`-` and `_` to get to an even 64 characters. This avoids issues with script injecting and homographic attacks.
97+
`-`, `_` and `+` to get at least 64 character options. This avoids issues with
98+
[script injecting](https://en.wikipedia.org/wiki/Code_injection) and
99+
[homographic attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack).
53100

54101
We also need to consider takeover attacks where an attacker takes a `short_url` that is similar to another
55102
short one in order to trick unsuspecting users (ex: `example` and `Example`). We can achieve this by only
56103
allowing custom slugs to be lower case. The reason not to down case incoming slugs is that it would both increase
57-
our computation time, doing the actual down case, and it would reduce our available characters by 26.
104+
our computation time, doing the down case on every request, and it would reduce our available characters by 26.
58105

59106
Another approach would be to do the lookup by a `short_url` as given, and if it misses in the database, try
60107
again after down casing. This would work and maintain our full character set, but it would violate our first
61108
consideration - keep this route fast. In order to maintain speed, we should also avoid running multiple SQL queries to determine the `full_url`. This is done by not allowing users to enter custom slugs with upper case letters so a down case is not necessary as a second query.
62109

110+
#### Scale
111+
112+
The final consideration is how this design will scale. The product requirements give no bound to the scale of
113+
the application. This means the design must strike a balance between speed of development, complexity of the
114+
solution and potential future scaling.
115+
116+
Based on 65 possible characters and a default of six characters for a random short, there are
117+
75.4 billion possible shorts.
118+
119+
```math
120+
65^5 = 75,418,890,625
121+
```
122+
123+
This provides the balance between available options and our choice of data store, PostgreSQL.
124+
125+
While the read may be fast, we should also consider the speed of the create. If we only randomly generate
126+
a code and then check to ensure it's not taken, we will slow down dramatically as the number of shorts grow.
127+
Said another way, the naive approach to code generation is `O(n)` for `n` existing shorts.
128+
An ideal solution would be able to generate a random that's in order `O(1)`. This is an optimization that
129+
we will aim to develop in the future.
130+
63131
## Project Design
64132

65133
This section outlines aspects of the application design that relate to the project requirements.
@@ -68,14 +136,22 @@ This section outlines aspects of the application design that relate to the proje
68136

69137
[![GitHub Actions Badge](https://img.shields.io/badge/-GitHub_Actions-4b93e6?style=flat&labelColor=2088FF&logo=github-actions&logoColor=white)](https://github.com/truggeri/rails-url-shortener/actions)
70138

71-
The testing suite is run as part of continuous integration using GitHub Actions.
139+
The testing suite and linting are run as part of
140+
[continuous integration](https://en.wikipedia.org/wiki/Continuous_integration)
141+
[using GitHub Actions](https://github.com/truggeri/rails-url-shortener/actions).
72142

73-
To run the test suite locally,
143+
The test suite can also be run locally.
74144

75145
```bash
76146
bundle exec rspec spec/
77147
```
78148

149+
And linting can also be done locally.
150+
151+
```bash
152+
bundle exec rubocop -D
153+
```
154+
79155
### README File
80156

81157
This project includes a [root README file](../README.md) which references this document as well as others.

0 commit comments

Comments
 (0)