Closed halfmoonui closed 7 months ago
I think #975 (Where applicable) should be covered in this issue as well.. Here are some general notes going through the codebase
(https://docs.djangoproject.com/en/5.0/releases/5.0/#database-generated-model-field)
TicketTier.tx_type
Ticket.extra_party
TierFiat.price_per_ticket_cents
Team.is_stripe_connected
Event.has_ended
Event.description_html
Event.localized_address_display
.choices
: https://docs.djangoproject.com/en/5.0/releases/5.0/#more-options-for-declaring-field-choicesUSE_L10N
setting: https://docs.djangoproject.com/en/5.0/releases/5.0/#features-removed-in-5-0Also some good resources on 5.0
.count
in properties, see if can be persisted. Generally, debate is read versus write (Persisting values vs using caches)In general this issue should focus less on optimizations and moreso on DX
At this stage, SocialPass is a stable, mature product. Its list of features is close to the chief competitors on the market, and we have been able to successfully host quite a few events without any big hiccups. I think this is a good time to at least start a discussion about issues we've noticed in the codebase, with the goal of documenting and solving them for the following benefits:
Issue 1: Too many useless abstractions
This is obviously a weird one because we all know the importance of DRY in programming. However, in the SocialPass codebase, there are quite a few places where we abstract things in a way that brings no benefit to the readability or performance. In fact, I strongly feel it actively hurts the readability of the codebase. For example on the
Event
model:The
clean_handle_google_event_class()
method is called exactly in one place in the entire codebase (in theclean()
) method. This is not really an abstraction. All it does is just make that block of code into a function and call it once. We would be better served with inlining that entire function in theclean()
method and just add a line of comment on top of the block to signify what is happening. Why? Because inlined code is genuinely easier to read in most practical scenarios. This example is easy to read because the function is defined right on top of its calling block, but imagine this being defined in a separate file in a separate app. That's rough, and we have a lot of that too: functions that are called exactly in one place defined in a separate file.I know a lot of this came from one particular engineer at Bix, and maybe this a valid design pattern. But now that we have a stability of product and features, we should make necessary changes to make the codebase more readable. I really like the Rule of Three where we only abstract something if it gets repeated thrice: https://lostechies.com/derickbailey/2012/10/31/abstraction-the-rule-of-three/
Issue 2: Unoptimized database models and querying
Take this
TicketTier
model and related objects as an example:The
TierFree
andTierBlockchain
models are entirely useless (no fields or only deprecated fields). They are only used as Boolean checks to find out what type ofTicketTier
the parent object is. TheTierFiat
only stores aprice
. The main issue I have with setups like these is that in order to get anything useful from objects, we need to query an extra 2-3 objects every time. This is evident from our views, where we useselect_related
andprefetch_related
everywhere. For example, on thetx_type
property, you will notice that we need to query for the 4 extra objects in order to get the type ofTicketTier
.Some of this is obviously unavoidable: we need a robust database table structure to make everything possible. However, we should also put in some thoughts into some easy optimizations. For example:
On the
TicketTier
model, when an object is created, why not save thetx_type
in a field? During create, we run a pre-save signal to find out the type and store it in a field. This type is never ever going to change. So the next time we need thetx_type
of aTicketTier
, we don't need to query for 4 otherTier*
tables.Another example:
We should possibly save the
total_price
in a field during create as it's not going to change. We don't allow customers to change checkout sessions (which expire anyway). We let them go back and create a new one.The great thing is that I hope that with Django 5, generated fields and #975 will solve a lot of these issues. However, we should also put in some thought to solving the easy performance wins.