1669014960
Imagine you are a Django developer building a social network for a lean startup. The CEO is pressuring your team for an MVP. The engineers have agreed to build the product using behavior-driven development (BDD) to deliver fast and efficient results. The product owner gives you the first feature request, and following the practice of all good programming methodologies, you begin the BDD process by writing a test. Next you code a bit of functionality to make your test pass and you consider your design. The last step requires you to analyze the feature itself. Does it belong in your app?
We can't answer that question for you, but we can teach you when to ask it. In the following tutorial, we walk you through the BDD development cycle by programming an example feature using Django and Aloe. Follow along to learn how you can use the BDD process to help catch and fix poor designs quickly while programming a stable app.
By the time you complete this tutorial, you should be able to:
Want to build this project as you read the post?
Start by:
Then, install the following dependencies and start a new Django project:
(venv)$ pip install \
django==3.2.4 \
djangorestframework==3.12.4 \
aloe_django==0.2.0
(venv)$ django-admin startproject example_bdd .
(venv)$ python manage.py startapp example
You may need to manually install setuptools-scm (
pip install setuptools-scm
) if you get this error when trying to installaloe_django
:distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('setuptools_scm')
Update the INSTALLED_APPS
list in settings.py:
INSTALLED_APPS = [
...
'aloe_django',
'rest_framework',
'example',
]
Just looking for the code? Grab it from the repo.
Behavior-driven development is a way of testing your code that challenges you to constantly revisit your design. When you write a test, you answer the question Does my code do what I expect it to do? through assertions. Failing tests expose the mistakes in your code. With BDD, you analyze a feature: Is the user experience what I expect it to be? There's nothing as concrete as a failing test to expose a bad feature, but the consequences of delivering a bad experience are tangible.
Execute BDD as part of your test development cycle. Draw the functional boundaries of a feature with tests. Create code that colors in the details. Step back and consider your design. And then do it all over again until the picture is complete.
Review the following post for a more in-depth explanation of BDD.
"Users should be able to log into the app and see a list of their friends."
That's how your product manager starts the conversation about the app's first feature. It's not much but you can use it to write a test. She's actually requesting two pieces of functionality:
Here's a rule of thumb: treat a conjunction like a beacon, warning you against trying to test too many things at once. If you ever see an "and" or an "or" in a test statement, you should break that test into smaller ones.
With that truism in mind take the first half of the feature request and write a test scenario: a user can log into the app. In order to support user authentication, your app must store user credentials and give users a way to access their data with those credentials. Here's how you translate those criteria into an Aloe .feature file.
example/features/friendships.feature
Feature: Friendships
Scenario: A user can log into the app
Given I empty the "User" table
And I create the following users:
| id | email | username | password |
| 1 | annie@example.com | Annie | pAssw0rd! |
When I log in with username "Annie" and password "pAssw0rd!"
Then I am logged in
An Aloe test case is called a feature. You program features using two files: a Feature file and a Steps file.
Feature
keyword to label the feature and the Scenario
keyword to define a user story that you are planning to test. In the example above, the scenario defines a series of steps that explain how to populate the User database table, log a user into the app, and validate the login. All step statements must begin with one of four keywords: Given
, When
, Then
, or And
.You may need to add an __init__.py file to the "features" directory for the interpreter to load the friendships_steps.py file correctly.
Run python manage.py harvest
and see the following output.
nosetests --verbosity=1
Creating test database for alias 'default'...
E
======================================================================
ERROR: A user can log into the app (example.features.friendships: Friendships)
----------------------------------------------------------------------
Traceback (most recent call last):
File "django-aloe-bdd/venv/lib/python3.9/site-packages/aloe/registry.py", line 151, in wrapped
return function(*args, **kwargs)
File "django-aloe-bdd/example/features/friendships.feature", line 5, in A user can log into the app
Given I empty the "User" table
File "django-aloe-bdd/venv/lib/python3.9/site-packages/aloe/registry.py", line 151, in wrapped
return function(*args, **kwargs)
File "django-aloe-bdd/venv/lib/python3.9/site-packages/aloe/exceptions.py", line 44, in undefined_step
raise NoDefinitionFound(step)
aloe.exceptions.NoDefinitionFound: The step r"Given I empty the "User" table" is not defined
-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: KqueueSelector
--------------------- >> end captured logging << ---------------------
----------------------------------------------------------------------
Ran 1 test in 0.506s
FAILED (errors=1)
Destroying test database for alias 'default'...
The test fails because you haven't mapped the step statements to Python functions. Do so in the following file.
example/features/friendships_steps.py
from aloe import before, step, world
from aloe.tools import guess_types
from aloe_django.steps.models import get_model
from django.contrib.auth.models import User
from rest_framework.test import APIClient
@before.each_feature
def before_each_feature(feature):
world.client = APIClient()
@step('I empty the "([^"]+)" table')
def step_empty_table(self, model_name):
get_model(model_name).objects.all().delete()
@step('I create the following users:')
def step_create_users(self):
for user in guess_types(self.hashes):
User.objects.create_user(**user)
@step('I log in with username "([^"]+)" and password "([^"]+)"')
def step_log_in(self, username, password):
world.is_logged_in = world.client.login(username=username, password=password)
@step('I am logged in')
def step_confirm_log_in(self):
assert world.is_logged_in
Each statement is mapped to a Python function via a @step()
decorator. For example, Given I empty the "User" table
will trigger the step_empty_table()
function to run. In this case, the string "User"
will be captured and passed to the function as the model_name
parameter. The Aloe API includes a special global variable called world
that can be used to store and retrieve data between test steps. Notice how the world.is_logged_in
variable is created in step_log_in()
and then accessed in step_confirm_log_in()
. Aloe also defines a special @before
decorator to execute functions before tests run.
One last thing: Consider the structure of the following statement:
And I create the following users:
| id | email | username | password |
| 1 | annie@example.com | Annie | pAssw0rd! |
With Aloe, you can represent lists of dictionaries using a tabular structure. You can then access the data using self.hashes
. Wrapping self.hashes
in the guess_types()
function returns the list with the dictionary values correctly typed. In the case of this example, guess_types(self.hashes)
returns this code.
[{'id': 1, 'email': 'annie@example.com', 'username': 'Annie', 'password': 'pAssw0rd!'}]
Run the Aloe test suite with the following command and see all tests pass.
(venv)$ python manage.py harvest
nosetests --verbosity=1
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.512s
OK
Destroying test database for alias 'default'...
Write a test scenario for the second part of the feature request: a user can see a list of friends.
example/features/friendships.feature
Scenario: A user can see a list of friends
Given I empty the "Friendship" table
When I get a list of friends
Then I see the following response data:
| id | email | username |
Before you run the Aloe test suite, modify the first scenario to use the keyword Background
instead of Scenario
. Background is a special type of scenario that is run once before every block defined by Scenario
in the Feature file. Every scenario needs to start with a clean slate and using Background
refreshes the data every time it is run.
example/features/friendships.feature
Feature: Friendships
Background: Set up common data
Given I empty the "User" table
And I create the following users:
| id | email | username | password |
| 1 | annie@example.com | Annie | pAssw0rd! |
| 2 | brian@example.com | Brian | pAssw0rd! |
| 3 | casey@example.com | Casey | pAssw0rd! |
When I log in with username "Annie" and password "pAssw0rd!"
Then I am logged in
Scenario: A user can see a list of friends
Given I empty the "Friendship" table
And I create the following friendships:
| id | user1 | user2 |
| 1 | 1 | 2 |
# Annie and Brian are now friends.
When I get a list of friends
Then I see the following response data:
| id | email | username |
| 2 | brian@example.com | Brian |
Now that you're dealing with friendships between multiple users, add a couple new user records to the database to start. The new scenario clears all entries from a "Friendship" table and creates one new record to define a friendship between Annie and Brian. Then it calls an API to retrieve a list of Annie's friends and it confirms that the response data includes Brian.
The first step is to create a Friendship
model. It's simple: It just links two users together.
example/models.py
from django.conf import settings
from django.db import models
class Friendship(models.Model):
user1 = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='user1_friendships'
)
user2 = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='user2_friendships'
)
Make a migration and run it.
(venv)$ python manage.py makemigrations
(venv)$ python manage.py migrate
Next, create a new test step for the I create the following friendships:
statement.
example/features/friendships_steps.py
@step('I create the following friendships:')
def step_create_friendships(self):
Friendship.objects.bulk_create([
Friendship(
id=data['id'],
user1=User.objects.get(id=data['user1']),
user2=User.objects.get(id=data['user2'])
) for data in guess_types(self.hashes)
])
Add the Friendship
model import to the file.
from ..models import Friendship
Create an API to get a list of the logged-in user's friends. Create a serializer to handle the representation of the User
resource.
example/serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'username',)
read_only_fields = fields
Create a manager to handle table-level functionality for your Friendship
model.
example/models.py
# New import!
from django.db.models import Q
class FriendshipManager(models.Manager):
def friends(self, user):
"""Get all users that are friends with the specified user."""
# Get all friendships that involve the specified user.
friendships = self.get_queryset().select_related(
'user1', 'user2'
).filter(
Q(user1=user) |
Q(user2=user)
)
def other_user(friendship):
if friendship.user1 == user:
return friendship.user2
return friendship.user1
return map(other_user, friendships)
The friends()
function retrieves all of the friendships that the specified user shares with other users. Then it returns a list of those other users. Add objects = FriendshipManager()
to the Friendship
model.
Create a simple ListAPIView
to return a JSON-serialized list of your User
resources.
example/views.py
from rest_framework.generics import ListAPIView
from .models import Friendship
from .serializers import UserSerializer
class FriendsView(ListAPIView):
serializer_class = UserSerializer
def get_queryset(self):
return Friendship.objects.friends(self.request.user)
Finally, add a URL path.
example_bdd/urls.py
from django.urls import path
from example.views import FriendsView
urlpatterns = [
path('friends/', FriendsView.as_view(), name='friends'),
]
Create the remaining Python step functions: One to call your new API and another generic function to confirm response payload data. (We can reuse this function to check any payload.)
example/features/friendships_steps.py
@step('I get a list of friends')
def step_get_friends(self):
world.response = world.client.get('/friends/')
@step('I see the following response data:')
def step_confirm_response_data(self):
response = world.response.json()
if isinstance(response, list):
assert guess_types(self.hashes) == response
else:
assert guess_types(self.hashes)[0] == response
Run the tests and watch them pass.
(venv)$ python manage.py harvest
Think of another test scenario. Users with no friends should see an empty list when they call the API.
example/features/friendships.feature
Scenario: A user with no friends sees an empty list
Given I empty the "Friendship" table
# Annie has no friends.
When I get a list of friends
Then I see the following response data:
| id | email | username |
No new Python functions are required. You can reuse all of your steps! Tests pass without any intervention.
You need one last piece of functionality to get this feature off the ground. Users can get a list of their friends, but how do they make new friends? Here's a new scenario: "a user should be able to add another user as a friend." Users should be able to call an API to create a friendship with another user. You know the API works if a record gets created in the database.
example/features/friendships.feature
Scenario: A user can add a friend
Given I empty the "Friendship" table
When I add the following friendship:
| user1 | user2 |
| 1 | 2 |
Then I see the following rows in the "Friendship" table:
| user1 | user2 |
| 1 | 2 |
Create the new step functions.
example/features/friendships_steps.py
@step('I add the following friendship:')
def step_add_friendship(self):
world.response = world.client.post('/friendships/', data=guess_types(self.hashes[0]))
@step('I see the following rows in the "([^"]+)" table:')
def step_confirm_table(self, model_name):
model_class = get_model(model_name)
for data in guess_types(self.hashes):
has_row = model_class.objects.filter(**data).exists()
assert has_row
Extend the manager and do some refactoring.
example/models.py
class FriendshipManager(models.Manager):
def friendships(self, user):
"""Get all friendships that involve the specified user."""
return self.get_queryset().select_related(
'user1', 'user2'
).filter(
Q(user1=user) |
Q(user2=user)
)
def friends(self, user):
"""Get all users that are friends with the specified user."""
friendships = self.friendships(user)
def other_user(friendship):
if friendship.user1 == user:
return friendship.user2
return friendship.user1
return map(other_user, friendships)
Add a new serializer to render the Friendship
resources.
example/serializers.py
class FriendshipSerializer(serializers.ModelSerializer):
class Meta:
model = Friendship
fields = ('id', 'user1', 'user2',)
read_only_fields = ('id',)
Add a new view.
example/views.py
class FriendshipsView(ModelViewSet):
serializer_class = FriendshipSerializer
def get_queryset(self):
return Friendship.objects.friendships(self.request.user)
Add a new URL.
example/urls.py
path('friendships/', FriendshipsView.as_view({'post': 'create'})),
Your code works and the tests pass!
Now that you've successfully programmed and tested your feature, it's time to analyze it. Two users become friends when one user adds the other one. This is not ideal behavior. Maybe the other user doesn't want to be friends -- don't they get a say? A user should request a friendship with another user, and the other user should be able to accept or reject that friendship.
Revise the scenario where a user adds another user as a friend: "a user should be able to request a friendship with another user."
Replace Scenario: A user can add a friend
with this one.
example/features/friendships.feature
Scenario: A user can request a friendship with another user
Given I empty the "Friendship" table
When I request the following friendship:
| user1 | user2 |
| 1 | 2 |
Then I see the following response data:
| id | user1 | user2 | status |
| 3 | 1 | 2 | PENDING |
Refactor your test step to use a new API, /friendship-requests/
.
example/features/friendships_steps.py
@step('I request the following friendship:')
def step_request_friendship(self):
world.response = world.client.post('/friendship-requests/', data=guess_types(self.hashes[0]))
Start by adding a new status
field to the Friendship
model.
example/models.py
class Friendship(models.Model):
PENDING = 'PENDING'
ACCEPTED = 'ACCEPTED'
REJECTED = 'REJECTED'
STATUSES = (
(PENDING, PENDING),
(ACCEPTED, ACCEPTED),
(REJECTED, REJECTED),
)
objects = FriendshipManager()
user1 = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='user1_friendships'
)
user2 = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='user2_friendships'
)
status = models.CharField(max_length=8, choices=STATUSES, default=PENDING)
Friendships can be ACCEPTED
or REJECTED
. If the other user has not taken action, then the default status is PENDING
.
Make a migration and migrate the database.
(venv)$ python manage.py makemigrations
(venv)$ python manage.py migrate
Rename the FriendshipsView
to FriendshipRequestsView
.
example/views.py
class FriendshipRequestsView(ModelViewSet):
serializer_class = FriendshipSerializer
def get_queryset(self):
return Friendship.objects.friendships(self.request.user)
Replace the old URL path with the new one.
example/urls.py
path('friendship-requests/', FriendshipRequestsView.as_view({'post': 'create'}))
Add new test scenarios to test the accept and reject actions.
example/features/friendships.feature
Scenario: A user can accept a friendship request
Given I empty the "Friendship" table
And I create the following friendships:
| id | user1 | user2 | status |
| 1 | 2 | 1 | PENDING |
When I accept the friendship request with ID "1"
Then I see the following response data:
| id | user1 | user2 | status |
| 1 | 2 | 1 | ACCEPTED |
Scenario: A user can reject a friendship request
Given I empty the "Friendship" table
And I create the following friendships:
| id | user1 | user2 | status |
| 1 | 2 | 1 | PENDING |
When I reject the friendship request with ID "1"
Then I see the following response data:
| id | user1 | user2 | status |
| 1 | 2 | 1 | REJECTED |
Add new test steps.
example/features/friendships_steps.py
@step('I accept the friendship request with ID "([^"]+)"')
def step_accept_friendship_request(self, pk):
world.response = world.client.put(f'/friendship-requests/{pk}/', data={
'status': Friendship.ACCEPTED
})
@step('I reject the friendship request with ID "([^"]+)"')
def step_reject_friendship_request(self, pk):
world.response = world.client.put(f'/friendship-requests/{pk}/', data={
'status': Friendship.REJECTED
})
Add one more URL path. Users need to target the specific friendship they want to accept or reject.
example/urls.py
path('friendship-requests/<int:pk>/', FriendshipRequestsView.as_view({'put': 'partial_update'}))
Update Scenario: A user can see a list of friends
to include the new status
field.
example/features/friendships.feature
Scenario: A user can see a list of friends
Given I empty the "Friendship" table
And I create the following friendships:
| id | user1 | user2 | status |
| 1 | 1 | 2 | ACCEPTED |
# Annie and Brian are now friends.
When I get a list of friends
Then I see the following response data:
| id | email | username |
| 2 | brian@example.com | Brian |
Add one more scenario after Scenario: A user can see a list of friends
to test filtering on the status. A user's friends consist of people who have accepted friendship requests from the user. Those who have not taken action or who have rejected the requests are not considered.
example/features/friendships.feature
Scenario: A user with no accepted friendship requests sees an empty list
Given I empty the "Friendship" table
And I create the following friendships:
| id | user1 | user2 | status |
| 1 | 1 | 2 | PENDING |
| 2 | 1 | 3 | REJECTED |
When I get a list of friends
Then I see the following response data:
| id | email | username |
Edit the step_create_friendships()
function to implement the status
field on the Friendship
model.
example/features/friendships_steps.py
@step('I create the following friendships:')
def step_create_friendships(self):
Friendship.objects.bulk_create([
Friendship(
id=data['id'],
user1=User.objects.get(id=data['user1']),
user2=User.objects.get(id=data['user2']),
status=data['status']
) for data in guess_types(self.hashes)
])
And also edit the FriendshipSerializer
serializer to implement the status field on the Friendship
model.
example/serializers.py
class FriendshipSerializer(serializers.ModelSerializer):
class Meta:
model = Friendship
fields = ('id', 'user1', 'user2', 'status',)
read_only_fields = ('id',)
Complete the filtering by adjusting the friends()
method on the manager.
example/models.py
def friends(self, user):
"""Get all users that are friends with the specified user."""
friendships = self.friendships(user).filter(status=Friendship.ACCEPTED)
def other_user(friendship):
if friendship.user1 == user:
return friendship.user2
return friendship.user1
return map(other_user, friendships)
Feature complete!
If you take one thing from this post, I hope it's this: Behavior-driven development is as much about feature analysis as it is about writing, testing, and designing code. Without that crucial step, you're not creating software, you're just programming. BDD is not the only way to produce software, but it's a good one. And if you're practicing BDD with a Django project, give Aloe a try.
Grab the code from the repo.
Original article source at: https://testdriven.io/
1626077187
Django is one of the popular python based open-source web frameworks mainly used by the developers who like to have rapid development along with the clean pragmatic design.
Read this blog to know the various Django Features with details.
#django framework #django web development #django development company #django development services #python django development company #python django development
1620177818
Welcome to my blog , hey everyone in this article you learn how to customize the Django app and view in the article you will know how to register and unregister models from the admin view how to add filtering how to add a custom input field, and a button that triggers an action on all objects and even how to change the look of your app and page using the Django suit package let’s get started.
#django #create super user django #customize django admin dashboard #django admin #django admin custom field display #django admin customization #django admin full customization #django admin interface #django admin register all models #django customization
1616147643
Django is a highly powerful framework for building small and large scale web applications. Being a Python-based framework, Django for web development is the preferred choice of companies all over the world.
If you are looking for proof of Django’s popularity, here it is - Django framework for web development is used by YouTube, Dropbox, Instagram, Pinterest, Google, Reddit, and many more. Being an open-source and maintainable web application development framework, companies adore it for building dynamic and sophisticated web applications.
Now you must be asking yourself “is Django good for web development?” The framework has a lot of benefits over other frameworks like Ruby on Rails, Flutter, Xamarin, etc. When you want a robust, scalable and stable application with a large codebase, using Django for web development is the right choice.
Read more: Pros and Cons of Django Web Framework for App Development
Django is admired by web developers all over the world. It is an easy-to-use battery including a framework that doesn’t eat up a lot of time in configuration and setting up the development environment.
The following benefits are your answer to “why use Django for web development”:-
Simplicity
Django framework for web development is excellent because of its simplicity and the ease of use. It allows developers to do more than one iteration and make modifications without running the entire code from scratch.
Apart from that, it makes development simpler with its Don’t Repeat Yourself Philosophy, allowing developers to reuse the codebase for creating different components.
Ecosystem
Django has a rich and diverse development ecosystem. It permits a lot of third-party apps and integrations. While setting up the ide for Django web development, you can use the third-party apps and integrations to add components or blocks in the web application.
From authorization to sending emails, there are apps for some of the most common features. It reduces the time taken to write code for these programs and leads to the timely delivery of products.
Documentation
Django for web development comes with excellent documentation to develop real-world applications. While other frameworks have alphabetical lists and modules, Django provides quick references to developers for building applications from different stages in the process.
The documentation is updated regularly as Python programmers add new exceptions, rules, and features to the open-source framework. While it is hard to keep it fine-tuned, it is one of the best documentation of a framework.
Wide community
If you are still asking what are the advantages of using Django for web development, then look at its wide community of developers. Django developers are increasing every day as the framework becomes a popular choice amongst enterprises to fulfill their web application needs.
The community is ready to assist on all Python Django web development projects. There are regular meets, tutorials, and material that can help anyone working with Django. The community regularly answers queries, and you can find the solution to almost any Django problem.
Libraries
Django allows developers to use massive amounts of libraries for building feature-rich and functional applications. Django REST framework is an excellent library for building Application Programming Interfaces (APIs). Django CMS, Django-allauth, and many other libraries are used for adding functionalities to web applications.
Since the framework uses Python, using libraries becomes an easy task. Python has a lot of libraries with Django integration that enables building quality applications.
Also Read: A Guide to Django Web Development for your Business
Whether you are looking for an ERP application or a B2C web app, Django framework for web development is the perfect solution. It allows you to develop scalable applications capable of handling heavy traffic load in the future.
You can hire Python developers and Django experts for Python development services from BoTree Technologies who can fulfill your web development needs.
Source: https://www.apsense.com/article/why-should-you-use-django-for-web-development.html
#python development services #django framework #django rest framework #django for web development #django for web development #django
1595059664
With more of us using smartphones, the popularity of mobile applications has exploded. In the digital era, the number of people looking for products and services online is growing rapidly. Smartphone owners look for mobile applications that give them quick access to companies’ products and services. As a result, mobile apps provide customers with a lot of benefits in just one device.
Likewise, companies use mobile apps to increase customer loyalty and improve their services. Mobile Developers are in high demand as companies use apps not only to create brand awareness but also to gather information. For that reason, mobile apps are used as tools to collect valuable data from customers to help companies improve their offer.
There are many types of mobile applications, each with its own advantages. For example, native apps perform better, while web apps don’t need to be customized for the platform or operating system (OS). Likewise, hybrid apps provide users with comfortable user experience. However, you may be wondering how long it takes to develop an app.
To give you an idea of how long the app development process takes, here’s a short guide.
_Average time spent: two to five weeks _
This is the initial stage and a crucial step in setting the project in the right direction. In this stage, you brainstorm ideas and select the best one. Apart from that, you’ll need to do some research to see if your idea is viable. Remember that coming up with an idea is easy; the hard part is to make it a reality.
All your ideas may seem viable, but you still have to run some tests to keep it as real as possible. For that reason, when Web Developers are building a web app, they analyze the available ideas to see which one is the best match for the targeted audience.
Targeting the right audience is crucial when you are developing an app. It saves time when shaping the app in the right direction as you have a clear set of objectives. Likewise, analyzing how the app affects the market is essential. During the research process, App Developers must gather information about potential competitors and threats. This helps the app owners develop strategies to tackle difficulties that come up after the launch.
The research process can take several weeks, but it determines how successful your app can be. For that reason, you must take your time to know all the weaknesses and strengths of the competitors, possible app strategies, and targeted audience.
The outcomes of this stage are app prototypes and the minimum feasible product.
#android app #frontend #ios app #minimum viable product (mvp) #mobile app development #web development #android app development #app development #app development for ios and android #app development process #ios and android app development #ios app development #stages in app development
1602979200
For a developer, becoming a team leader can be a trap or open up opportunities for creating software. Two years ago, when I was a developer, I was thinking, “I want to be a team leader. It’s so cool, he’s in charge of everything and gets more money. It’s the next step after a senior.” Back then, no one could tell me how wrong I was. I had to find it out myself.
I’m naturally very organized. Whatever I do, I try to put things in order, create systems and processes. So I’ve always been inclined to take on more responsibilities than just coding. My first startup job, let’s call it T, was complete chaos in terms of development processes.
Now I probably wouldn’t work in a place like that, but at the time, I enjoyed the vibe. Just imagine it — numerous clients and a team leader who set tasks to the developers in person (and often privately). We would often miss deadlines and had to work late. Once, my boss called and asked me to come back to work at 8 p.m. to finish one feature — all because the deadline was “the next morning.” But at T, we were a family.
We also did everything ourselves — or at least tried to. I’ll never forget how I had to install Ubuntu on a rack server that we got from one of our investors. When I would turn it on, it sounded like a helicopter taking off!
At T, I became a CTO and managed a team of 10 people. So it was my first experience as a team leader.
Then I came to work at D — as a developer. And it was so different in every way when it came to processes.
They employed classic Scrum with sprints, burndown charts, demos, story points, planning, and backlog grooming. I was amazed by the quality of processes, but at first, I was just coding and minding my own business. Then I became friends with the Scrum master. I would ask him lots of questions, and he would willingly answer them and recommend good books.
My favorite was Scrum and XP from the Trenches by Henrik Kniberg. The process at D was based on its methods. As a result, both managers and sellers knew when to expect the result.
Then I joined Skyeng, also as a developer. Unlike my other jobs, it excels at continuous integration with features shipped every day. Within my team, we used a Kanban-like method.
We were also lucky to have our team leader, Petya. At our F2F meetings, we could discuss anything, from missing deadlines to setting up a task tracker. Sometimes I would just give feedback or he would give me advice.
That’s how Petya got to know I’d had some management experience at T and learned Scrum at D.
So one day, he offered me to host a stand-up.
#software-development #developer #dev-team-leadership #agile-software-development #web-development #mobile-app-development #ios-development #android-development